Compare commits

...

103 Commits

Author SHA1 Message Date
a26b0b60d1 Fix srcdir != builddir build of St.gir
We need to use st_cflags which has -I$(srcdir) not ST_CFLAGS
when building St.gir.
2009-10-07 19:32:00 -04:00
60dbb19c2d Reference Meta-2.28.gir
Now that Mutter has been bumped to 2.28.0, we need to reference
Meta-2.28.gir not Meta-2.27.gir.
2009-10-07 19:27:17 -04:00
3703a86354 Add run-test.sh to CLEANFILES
Fix distcheck by adding run-test.sh to CLEANFILES
2009-10-07 19:18:43 -04:00
992b43f914 Bump version to 2.28.0 2009-10-07 19:07:05 -04:00
ff39edd1ee Deal with unknown flags from ClutterEvent.get_state()
When we get a ClutterModifierType from Clutter, it might contain
bits not in the enumeration. See bug 59771 for a similar problem
with GdkModifierType.

Add a wrapper Shell.get_event_state() around clutter_event_get_state()
to mask these bits out and only return approved bits.

https://bugzilla.gnome.org/show_bug.cgi?id=597735
2009-10-07 17:22:37 -04:00
a81a16801d Fix overview to show on the correct monitor
When tweening the overview in, tween it to appear on the correct
position of the primary monitor, not at (0,0); position
the 'backOver' actor to cover all monitors properly.

Reported by Rui Matos

https://bugzilla.gnome.org/show_bug.cgi?id=597721
2009-10-07 17:14:56 -04:00
4c6f770dea Fix positioning of the Calendar on multihead
Parens were in the wrong place, resulting in the calendar not
being properly centered on the primary monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=597078
2009-10-07 14:52:49 -04:00
c0b01c0210 Match on menu category during application search
Being able to display all applications in a category based on the search
string with a category name is generaly useful.

Prepare all the applications that match a search term based on their
category name up-front.

Remove unused this._appCategories and a call to non-existing
itemInfo.get_categories()
2009-10-07 14:28:49 -04:00
400326e549 Add docs and (transfer none) to st_widget_get_theme_node()
st_widget_get_theme_node() was missing the (transfer none)
GObject Introspection causing crashes. Add that and document the
function.
2009-10-07 14:26:14 -04:00
d7af6d40e3 [AppSwitcher] Use thumbnails instead of a window menu, and other UI changes
https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-10-07 14:00:43 -04:00
90ebcd32e3 Fix exception when showing menu for not-running app
Need to check windows.length before checking first window.

https://bugzilla.gnome.org/show_bug.cgi?id=597466
2009-10-06 17:19:36 -04:00
53d0581377 Don't process the first click on Activities button for a timeout period after the hot corner was triggered
This avoids opening and closing the overview when the hot corner is triggered
and the activities button is clicked.
2009-10-06 16:58:30 -04:00
439daf828f [AppSwitcher] add a workaround for a gjs problem with GdkModifierType
gdk_display_get_pointer() sometimes returns values for the mask that
aren't part of the GdkModifierType enumeration, which gjs doesn't like
(bug 597292). Work around that by adding a C wrapper that strips out
the extra flags.

https://bugzilla.gnome.org/show_bug.cgi?id=597559
2009-10-06 16:07:37 -04:00
d120d03de9 Workaround being allocated 0x0
Because of a chain of bugs we could end up getting allocated 0x0,
and this would cause the WellGrid allocation code to be unhappy.

See http://bugzilla.openedhand.com/show_bug.cgi?id=1831

Simply treat 0 as "unlimited" i.e. equivalent to -1.

https://bugzilla.gnome.org/show_bug.cgi?id=597586
2009-10-06 15:12:20 -04:00
53fbabe2ca Use GtkIMContext instead of ClutterIMContext
Add StIMText, which is a drop-in replacement for ClutterIMText but
uses GtkIMContext instead of ClutterIMContext.

StIMText doesn't have preedit support (would need ClutterText
changes), so isn't going to be useful for complicated input methods,
but is good enough to get dead keys and similar working.

entry.js: Simple test case of StEntry
gnome-shell.modules: Remove clutter-imcontext module

https://bugzilla.gnome.org/show_bug.cgi?id=597471
2009-10-06 14:48:32 -04:00
4bdd40911f Disconnect from window user time notifications, only sort when mapped
Before we were badly leaking AppIcons by not disconnecting from the
window signal handlers when our actor got destroyed.  This caused
us to repeatedly re-sort the windows for each AppIcon that
had ever been displayed with obvious bad consequences.

Besides simply chaining the signals to the lifetime of the AppIcon
actor, we also only do the sorting if we're mapped.  This decreases
the amount of work to do in the not-mapped case.

https://bugzilla.gnome.org/show_bug.cgi?id=597120
2009-10-06 12:49:43 -04:00
ff4ac0d02e [AppIcon] Improve shell_draw_box_pointer()
Add a new enum type for the pointer direction, rather than abusing
ClutterGravity, and implement the missing directions.

https://bugzilla.gnome.org/show_bug.cgi?id=597498
2009-10-06 09:53:47 -04:00
45dd342cc0 [AppIcon] redo constructor to take a params object, add "size" param
Add a "size" parameter to allow changing the AppIcon size, and then
simplify the constructor by taking an object with parameters like
gobject-introspection constructors do, rather than taking a large
number of miscellaneous arguments.

https://bugzilla.gnome.org/show_bug.cgi?id=597498
2009-10-06 09:53:42 -04:00
e5efecd2bd [AppIcon] compute the sorted window list even if not doing the menu
https://bugzilla.gnome.org/show_bug.cgi?id=597498
2009-10-05 22:35:05 -04:00
3bf88b8988 Run dialog: Make error message translatable
Allow the error message to be translated.

https://bugzilla.gnome.org/show_bug.cgi?id=597422
2009-10-05 23:53:50 +02:00
4ddc1118bb Work around libcroco < 0.6.2 parsing bug for 'rgba'
To work around a problem where libcroco < 0.6.2 can't handle
functions starting with 'r' or 'u', preconvert 'rgba' to 'RGBA'
when parsing stylesheets and then check for rgba()
case-insensitively.

(libcroco is uniformly case-sensitive, though the CSS spec requires
that ASCII should be handled case-insensitively.)

https://bugzilla.gnome.org/show_bug.cgi?id=597054
2009-10-05 16:25:38 -04:00
fd1e7b2a0f Places: Add padding between "Connect to.." and devices section
Add padding after "Connect to.." to make the list look consistent when devices
are connected.

https://bugzilla.gnome.org/show_bug.cgi?id=597423
2009-10-05 22:24:04 +02:00
aed1e67add Check that no new term was introduced before doing a subsearch
We combine search terms with OR, so if a new search term is introduced,
the search results are no longer a subset of the previous search results.
2009-10-05 13:50:58 -04:00
dc99e8ffcd [AppSwitcher] Change separator color
https://bugzilla.gnome.org/show_bug.cgi?id=597362
2009-10-05 13:13:21 -04:00
6a8b50cb00 Don't create multiple copies of the (+) button
Currently we recreate it every time Main.overview.show() is called,
 so destroy it to avoid having multiple copies floating around.

https://bugzilla.gnome.org/show_bug.cgi?id=597309
2009-10-05 18:54:41 +02:00
edb50d5dc7 String formatting: Fix warning
Fix to not trigger a warning like:

JS ERROR: !!!   WARNING: 'anonymous function does not always return a value'

by adding a return ""; at the end of the anonymous function.

https://bugzilla.gnome.org/show_bug.cgi?id=595661
2009-10-05 17:59:33 +02:00
2f6c951997 Avoid doing expensive work when not mapped
For some unknown reason we were connecting to app-added and
app-removed on ShellAppMonitor in the AppDisplay class, which
never made any use of the data.  Simply don't connect to those
signals for now.  In the future we should have AppDisplay
be using the AppIcon class which will more correctly handle
dynamic changes.

In the AppWell, avoid doing the full relayout until we're
actually mapped.

https://bugzilla.gnome.org/show_bug.cgi?id=597169
2009-10-05 11:32:33 -04:00
64cd51667d Add String formatting
Add String formatting by extending the String object with a
format method.

Now we can do stuff like "Text: %s, %d".format(somevar, 5)

This is required for proper translation of some strings.

https://bugzilla.gnome.org/show_bug.cgi?id=595661
2009-10-04 23:37:33 +02:00
caa08f27fa Updated German translation 2009-10-04 18:52:10 +02:00
26015ef16d Select next window from the current app on alt+tab
This slightly changes the behaviour of the alt+tab window, this way:
when using alt-tab on a workspace that contains two or more windows from
the same window, the application selected when hitting alt+tab is the
currently selected application, but the highlighted window is the next
one.

Intended goal is to make it easier to cycle around windows of the same
application while not having to cycle through all the applications first.

https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-10-04 12:45:23 -04:00
4798ad5107 Consider workspace when sorting windows in menu.
By sorting the windows in the current workspace first, we ensure that
when using alt+tab to switch windows, we will pick a window on the
active workspace as the default focused window for the application if
the application has at least one window on the current workspace (that
is, if it is on the left of the app switcher separator).

This makes the behaviour of alt+tab more predictable for the user, as
an user will expect alt+tab to switch to the window he/she can see right
now rather than the one on the workspace he just left (presumably to do
something else on the workspace he's currently on).

https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-10-04 12:45:19 -04:00
9b05304c2d Use better fitting color for non ARGB tray icons
Currently we use 0xefefefff as a background color for non ARGB tray icons,
which looks out of place (i.e does not fit the panel's background gradient).

Change it to 0x0b0b0bff to fix that.

https://bugzilla.gnome.org/show_bug.cgi?id=597148
2009-10-04 10:02:37 +02:00
795feca393 Updated Arabic translation 2009-10-03 19:20:25 +02:00
31663dcd83 Updated Norwegian bokmål translation. 2009-10-03 10:48:19 +02:00
2f2df61093 Cache applications for a menu
Rather than recomputing this each time someone asks, have a
cache of the apps for a given menu.

https://bugzilla.gnome.org/show_bug.cgi?id=597167
2009-10-02 19:30:42 -04:00
3b8d53060d Immediately pop up menu on right click
We were actually showing a menu on button 3 before, but only
through a chain of coincidences.  This patch explicitly supports
it and makes sure we show it immediately rather than after
a timeout. Pass the activating button in so that we only pop
down on that button.

https://bugzilla.gnome.org/show_bug.cgi?id=596371
2009-10-02 19:28:25 -04:00
c5ce405859 Ignore releases of buttons other than the activating button
Before we hardcoded popdowns to only button 1 before. But we need
to actually pop down on the release of the activating button.
(Once the button is released, if the we don't pop-down the menu,
then subsequently we let the user use any button.)

https://bugzilla.gnome.org/show_bug.cgi?id=596371
2009-10-02 19:28:25 -04:00
b3a5fc72fb Add Add/Remove from favorites menu, unify lists more
Also have inactive applications pop up a menu.

Add/Remove from favorites is now in the menu.

Concatenate the favorites/not-favorites instead of having a gap only
if you happened to have a not-divisible-by-4 number of favorites.

https://bugzilla.gnome.org/show_bug.cgi?id=596371
2009-10-02 19:28:25 -04:00
8a2cc11cc0 Fix rundialog not closing for internal commands
Signed-off-by: Adel Gadllah <adel.gadllah@gmail.com>

https://bugzilla.gnome.org/show_bug.cgi?id=597076
2009-10-02 14:18:51 -04:00
cbb3a3aec8 Fix make dist by including all the private files and the tests in the tarball 2009-10-02 17:02:25 +01:00
e382da9708 [AppSwitcher] Update colors/border
Switch from Big.Box to St.Bin and update styling per latest spec

https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-10-02 09:04:04 -04:00
ab1fbbde92 add a missing (optional) ";" 2009-10-02 08:49:22 -04:00
5f5266ca60 [AppSwitcher] Display a separator between apps on this workspace and others.
This makes a visible distinction between the apps that only have minimized
windows on the current workspace and the ones that have no window on the
workspace.

https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-10-02 14:17:00 +02:00
68e8b14b8b [AppSwitcher] Use GenericContainer instead of BigBox.
This allows defining some custom policy for size allocation.
Currently, the minimum width is always used, but it can be tweaked
afterwards when a sizing policy has been defined.

https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-10-02 14:15:01 +02:00
abdd8b330c Don't show all sections each time the search is updated
We shouldn't show all sections each time the search is updated because that
breaks viewing search results for a single section and flickers headers for
sections with no results.
2009-10-01 18:21:27 -04:00
eb8176deeb Fix regressions in the item displays
Fix displaying documents in the document browse and refreshing the selection
when the results have changed.

Make sure we are passing the appropriate flag to _redisplay() in GenericDisplay.

Make sure we set this._appsStale to true if there was a change in the
applications set.

Don't call _refreshCache() from the AppDisplay constructor.

Don't short-circuit the call to _refreshCache() from _redisplay() on initial load.

Rename _redisplayFull() to recreateDisplayItems() and remove adding an
actor to the actual result list in _addDisplayItem() because we redo adding the
actors to the list in _redisplayReordering() anyway to ensure that we add
them in the right order.

Based on a patch from Colin Walters.
2009-10-01 17:41:17 -04:00
956f89f377 Add dep of clutter on gir-repository to moduleset
Clutter requires Pango.gir. Until Pango is universally available
in a version that builds its own GIR, Pango.gir is built as part
of gir-repository.

(Found by Adel Gadllah)
2009-10-01 17:34:28 -04:00
1c69380923 [AppSwitcher] Put apps with no window on current workspace at the end.
Following the idea expressed in bug 590563 by mccann ("Minimized or
hidden applications should appear at the end of the list"), we should
also put applications that have no visible window in the active
workspace at the end of the alt-tab window list, after apps which have
minimized windows in the active workspace.
2009-10-01 23:25:15 +02:00
d2bc7b200e Update Catalan translation. 2009-10-01 23:23:54 +02:00
afb3b1e718 Fixes for Calendar widget
Miscellaneous fixes from review:

- Distribute calendar.js and the interactive test
- Make the pointless protection against leap seconds actually work
  by starting in the middle of the day so that forward/back always
  move a day.
- Use a variable instead of an inline '8' to know where to start
  when removing old day actors.
- Remove a stray comment from the test

https://bugzilla.gnome.org/show_bug.cgi?id=596432
2009-10-01 16:48:24 -04:00
061a2cfbfb Add scroll-wheel support to the calendar
Make the calendar reactive and handle scroll events to change the month.
(GtkCalendar and hence the old gnome-panel calendar supported this and
it is apparently a handy way to flip through months.)

The padding is moved from the CalenderPopup to the Calendar so that the
scroll region extends all the way to the edge of the popup.

https://bugzilla.gnome.org/show_bug.cgi?id=596432
2009-10-01 16:48:24 -04:00
243824ab80 Replace "round(x)" with "(int)(0.5 + x)"
round() is a C99 addition, so causes portability problems:
different C library versions require different #defines to
enable it. So simply avoid using it.
2009-10-01 22:39:04 +02:00
90ddad7ba1 Make Connect string translatable
Signed-off-by: Adel Gadllah <adel.gadllah@gmail.com>

https://bugzilla.gnome.org/show_bug.cgi?id=597038
2009-10-01 16:11:33 -04:00
20b29ff48f Sync clone stacking order with MetaStackTracker
Previously, we initialized actor stacking order from the return
value of global.get_windows() once, which is defined to be in
stack order.  However it was not updated later.  Furthermore,
the way stackAbove was called from onAnimationComplete in
WindowClone was highly dubious, since there are lots of animations
which apply to the clones, and we want the stacking to be right
all of the time, not when some animation completes.

Fix this by connecting to 'restacked' on the screen and syncing
the clones.

I also snuck in another bugfix here; we weren't disconnecting
from the 'showing' signal handler, which had various bad
consequences.

https://bugzilla.gnome.org/show_bug.cgi?id=596263
2009-10-01 15:59:05 -04:00
96f4d318c5 Add libcroco to gnome-shell-build-setup.sh
Check for the new build dependency "libcroco" when running on any
of the distros supported by the script.
2009-10-01 21:57:53 +02:00
6f7da264ba Support LinuxMint in the build script
LinuxMint is based on Ubuntu, so can be handled with the
same package list as Debian and Ubuntu.

https://bugzilla.gnome.org/show_bug.cgi?id=596447
2009-10-01 15:40:04 -04:00
640e45c12a Make gnome-shell depend on clutter-imcontext
The gnome-shell module requires clutter-imcontext to build.
2009-10-01 15:05:18 -04:00
04e28cd7c4 Add a calendar pop-down to the clock
js/ui/calendar.js: Generic calendar widget
tests/interactive/calendar.js: Basic test of the calendar

js/ui/panel.js: Add a pop-down from the clock that shows a
  calendar widget. The pop-down is not menu-like to allow the user to
  interact with an application while looking at the calendar.
gnome-shell.css: Add theming for calendar, calendar popup, and for
  buttons on the panel

https://bugzilla.gnome.org/show_bug.cgi?id=596432
2009-10-01 15:05:11 -04:00
2cc41c6726 StButton: Fix property enumeration names
Property enumeration names should correspond exactly to the property names;
in particular the ACTIVE vs :checked disparity was confusing reading the
code.

http://bugzilla.moblin.org/show_bug.cgi?id=6504
2009-10-01 14:46:33 -04:00
03a45b665c testcommon.css: Don't theme all buttons
StButton is used for many things - scrollbar steppers, etc. Theming
all buttons to look like push-buttons breaks that. So in testcommon.css
just theme a .push-button class to look vaguely button-like.

https://bugzilla.gnome.org/show_bug.cgi?id=596432
2009-10-01 14:46:33 -04:00
5a42179a96 Port StTable to StThemeNode
Convert the StTable code from StStylable to StThemeNode. The
:row-spacing and :col-spacing GObject properties are converted
into spacing-rows and spacing-columns style properties.

A new interactive test is added for StTable.

https://bugzilla.gnome.org/show_bug.cgi?id=596811
2009-10-01 14:46:33 -04:00
af3ec56ca1 Handle adding children to StTable from Javascript
Remove the StTable specific methods to add actors:

 st_table_add_actor()
 st_table_add_actor_with_properties()

Since they shadow the generic ClutterContainer add_actor() method,
and patch in our add() convenience function as we do for
StBoxLayout.

https://bugzilla.gnome.org/show_bug.cgi?id=596811
2009-10-01 14:46:33 -04:00
289b19aa31 Import MxTable as StTable
Import table code from Mx library

https://bugzilla.gnome.org/show_bug.cgi?id=596811
2009-10-01 14:46:33 -04:00
91eb613d69 Turn StBoxLayout:spacing into a style property
Remove the StBoxLayout:spacing GObject property, and instead make
BoxLayout look up the spacing from the CSS style. This makes it
consistent with padding and will allow the use of units. (The
removal of the GObject property entirely instead of making it an
override is consistent with how we handle color, font, padding, etc.)

https://bugzilla.gnome.org/show_bug.cgi?id=596803
2009-10-01 14:46:32 -04:00
1c7c53d19f lookingGlass: Get font from GConf
Instead of using "Monospace", pick the users configured monospace font
name up from GConf. (This is a nice touch, but is more done here to
demonstrate that we can do it rather than for any great utility.)

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:46:32 -04:00
d4304495c6 Port LookingGlass console to ST widgets
* Style aspects like colors and fonts are moved into gnome-shell.css.
* Scrolling is adding using StScrollView.

Based on a patch from Colin Walters
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:46:32 -04:00
b77b205d37 Add clutter-text properties to StEntry and StLabel
Add clutter-text properties to allow getting access to the underlying
ClutterText actor. This corresponds to the get_clutter_text() methods.

The PROP_LABEL and PROP_ENTRY enum values are renamed to PROP_TEXT to
match the names of the properties that they correspond to, and the
properties of StEntry are reordered into alphabetical order.

Based on a patch from Colin Walters
https://bugzilla.gnome.org/show_bug.cgi?id=591245
http://bugzilla.moblin.org/show_bug.cgi?id=6313
2009-10-01 14:41:20 -04:00
a15205e6c4 Fix interaction of borders/background and scrolling
StBoxLayout: Make consistent that the area scrolled and clipped
to is the content area (excluding borders and padding.) Translate
back appropriately when chaining up so that the parent background
is drawn at the right place and picking on the box (if it's reactive)
picks at the right place on the screen.

clip-to-allocation is removed from StScrollView since it's just
not right - if the child has any non-moving elements, like headers or
borders, it will need to set a narrower clip. And even if the entire
child scrolls, we want to clip to an arrow that excludes the scrollbars.

https://bugzilla.gnome.org/show_bug.cgi?id=595997
2009-10-01 14:41:19 -04:00
28dbf7a06e Allocate children as wide as the scrolled area
When we are scrolling a vertical box horizontally , children should be
allocated horizontally as wide as the full horizontal scrolled area,
not just to the size of the "viewport". Similarly for a horizontal box.

http://bugzilla.moblin.org/show_bug.cgi?id=6312
https://bugzilla.gnome.org/show_bug.cgi?id=595996
2009-10-01 14:41:19 -04:00
1ec8e9eb6b Allow StBoxLayout to shrink down to its minimum size
When a StBoxLayout is allocated a size less than its natural size,
think "shrink" needs to be divided among the children that have
a smaller minimum size than natural size.

This is done by preferentially shrinking the children that are most
expanded from their minimum size and then increasing that set of
children until we've found enough total shrink.

A new method is used of allocating children at integral sizes - instead
of rounding the per-child extra amount to an integer (which causes
cumulative round-off errors), compute the position as we go along in
floats and round individually for each child widget.

Extend the box-layout test to include of a test of a box being set
to various widths, starting quite narrow.

http://bugzilla.moblin.org/show_bug.cgi?id=6311
https://bugzilla.gnome.org/show_bug.cgi?id=595995
2009-10-01 14:41:19 -04:00
fa09f7a6da Don't count not-visible children among expand children
When counting how many children we should divide extra space among,
don't count not-visible children.

http://bugzilla.moblin.org/show_bug.cgi?id=6310
https://bugzilla.gnome.org/show_bug.cgi?id=595995
2009-10-01 14:41:19 -04:00
d67e54d3ee Don't use the default stage when setting up adjustments
If the actor isn't in a stage, then setting up the adjustment
based on the actor's size (which we can't compute) and the
size of the default stage (which isn't relevant), doesn't make
sense. Just use arbitrary default values.

The adjustments will be updated to reasonable values when first
the box is first allocated.

It's not entirely clear to me why we ever want to compute the
adjustment settings this way; perhaps we should always use
default values.

http://bugzilla.moblin.org/show_bug.cgi?id=6307
https://bugzilla.gnome.org/show_bug.cgi?id=595996
2009-10-01 14:41:19 -04:00
d263c12e2e Match CSS for background extents
The CSS specification says that the background extends to the
edge of the border (settable in CSS3 with border-clip), make
BigRectangle match this by computing an "effective border color"
as 'border OVER background'.

(If we don't want this behavior - e.g., to be able to use the
transparent borders as margins, then alternatively transparent
border handling would have to be fixed in st-widget.c, since
prior to this transparent and translucent borders were handled
differently.)

https://bugzilla.gnome.org/show_bug.cgi?id=595993
2009-10-01 14:41:19 -04:00
4d55ccff39 Rename StThemeImage to StBorderImage
The current CSS3 border-image is close to a superset of what we were
doing for -hippo-background-image. Woot! rename StThemeImage to
StBorderImage and change parsing to look for:

 border-image: <url> <number>...

Rather than

 -st-background-image: <url> <length>...

percentanges for the border sizes are not currently supported, neither
are the keywords for handling of the middle part. We always do 'stretch'
for now.

https://bugzilla.gnome.org/show_bug.cgi?id=595990
2009-10-01 14:41:19 -04:00
2a0adc0fc8 Add support for colored borders
Use BigRectangle to draw the border and background if there's
a border width or border radius and no border image. (Only
uniform borders are supported for now with some deviations
from the CSS model noted in the comments.)

The background color and image parameters are removed from
StWidget's draw_background() method since they were not used
for StButton (the only current user) and the encapsulation
break that they presented caused some minor problems.

Add a test case for borders, and also use borders to style
the buttons in the 'inline-style' test case.

https://bugzilla.gnome.org/show_bug.cgi?id=595993
2009-10-01 14:41:19 -04:00
076e902b2c Centralize computations of border and padding into StThemeNode
Rather than repeating the computation of borders in many different
widget subclasses, add helper functions:

 st_theme_node_adjust_for_height()
 st_theme_node_adjust_preferred_width()
 st_theme_node_adjust_for_width()
 st_theme_node_adjust_preferred_height()
 st_theme_node_get_content_box()

That are used in get_preferred_width()/get_preferred_height() and
allocate() methods to consistently apply the necessary adjustments.
This allows removing the StPadding type.

Queueing a relayout when the borders/padding change is moved from
st_widget_real_style_changed() to the invoking code to allow access
to the old StThemeNode for comparison. (Should this be added as
a parameter to the signal?)

Borders are included in the geometry adjustments, but borders
are not yet drawn.

https://bugzilla.gnome.org/show_bug.cgi?id=595993
2009-10-01 14:41:19 -04:00
8c72623da3 StThemeNode: Add border-radius support
Add support for parsing and caching the border-radius property.
Different radii for the 4 corners are supported; elliptical corners
are not supported.

https://bugzilla.gnome.org/show_bug.cgi?id=595993
2009-10-01 14:41:18 -04:00
1fd25573e5 Fix problems with 4-sided padding: specifiers
The test for identifying such a specifier was wrong, and the last
value was assigned to the wrong sides.

https://bugzilla.gnome.org/show_bug.cgi?id=595990
2009-10-01 14:41:18 -04:00
6527dbc8b7 Add support for inline styles
Add support for passing an inline-style string when creating a
StThemeNode.

Hook this up to a new 'style' property of StWidget.

Add a test case that demonstrates using this to update font sizes
on the fly.

https://bugzilla.gnome.org/show_bug.cgi?id=595991
2009-10-01 14:41:18 -04:00
3c646ec516 run-test.sh: support running tests under gdb
As with the 'gnome-shell' -g/--debug can be passed to run under
the debugger.

https://bugzilla.gnome.org/show_bug.cgi?id=595987
2009-10-01 14:41:18 -04:00
a9fd350396 Port our imported parts of Mx to ShellTheme
ShellTheme replaces both StStyle and ccss_stylesheet_t.

The interface StStylable is replaced by usage of ShellThemeNode.
A concrete node class allows some significant optimizations of property
inheritance that would have been much more difficult to achieve with
the highly abstract pair of StStylable and ccss_node_t.

Some operations that were previously on StStylable (like the
::style-changed signal) are directly on NtkWidget.

Custom properties are no longer registered as param-specs; instead you
call directly into shell theme node to look up a length or color:

shell_theme_node_get_length (theme_node, "border-spacing", FALSE, &spacing);

The dependency on libccss is dropped, while preserving all existing
functionality and adding proper parsing and inheritance of font properties
and proper inheritance for the 'color' property.

Some more javascript tests for CSS functionality are added; workarounds for
a CSS bug where *.some-class was needed instead of .some-class are removed.

https://bugzilla.gnome.org/show_bug.cgi?id=595990
2009-10-01 14:41:18 -04:00
e91e8e993d Add emacs mode-lines to ST sources
To each .c and .h file, add:

 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

'gnu' is the default anyways for Emacs, but indent-tabs-mode is not,
so this sets things up to correspond to the policy of no-tabs.

http://bugzilla.moblin.org/show_bug.cgi?id=6467
2009-10-01 14:41:18 -04:00
276d9a9302 Import stylesheet code from hippo-canvas
Import:

  HippoCanvasTheme      => StTheme
  HippoCanvasThemeImage => StThemeImage
  HippoCanvasStyle      => StThemeNode

StThemeContext is a new class managing the theme for a stage and
global properties like resolution.

test-theme.c is a newly written test program to do verification of the
style matching and property handling rules.

Various changes are made in the import:

 - Comprehensive reindentation
 - guint32 pixels replaced with ClutterColor
 - General pseudo-class support added
 - Old-fashioned (non-bordered) background image support added, though
   with no support for repeat, etc.
 - Bug fixes for problems revealed by test program

https://bugzilla.gnome.org/show_bug.cgi?id=595990
2009-10-01 14:41:18 -04:00
6b95864076 Fix installation and distribution of stylesheet data
Install and distribute gnome-shell.css and theme images. They are moved
down from $datadir to $datadir/theme to avoid a weirdness where we have
images in $datadir and then also in $datadir/images.

(Also moved in the source tree to avoid adding another difference between
installed and uninstalled operation.)

https://bugzilla.gnome.org/show_bug.cgi?id=595989
2009-10-01 14:41:18 -04:00
d4c577a299 Remove stale C files
Remove several stale C files that we are no longer using; now
that we have a distcheck hook to catch non-distributed files, these
would otherwise prevent distchecking.

https://bugzilla.gnome.org/show_bug.cgi?id=595988
2009-10-01 14:41:17 -04:00
b90fc1e194 Extend distcheck for files in Git to all files
Instead of just checking that we distribute all Javascript files, check
that we distribute everything that is in Git.

The toplevel Makefile.am has a variable DIST_EXCLUDE that lists patterns
of files that we actually don't want to distribute.

https://bugzilla.gnome.org/show_bug.cgi?id=595988
2009-10-01 14:41:17 -04:00
55497899dd Add some structure for interactive tests of UI components
js/ui/environment.js: Split out initial UI setup (Tweener initialization,
  ClutterContainer monkey-patching) into a separate file we can import from tests.

tests/: Directory for various types of tests
tests/run-test.sh: Shell script that to run tests with an appropriate
  environment set up.

tests/testcommon/: Common modules and data for tests
tests/interactive/: Interactive tests

tests/interactive/box-layout.js: A sample test of StLayout

https://bugzilla.gnome.org/show_bug.cgi?id=595987
2009-10-01 14:41:17 -04:00
e37790fdf9 Monkey-patch in ClutterContainer methods for StBoxLayout
Setting options for children added to StBoxLayout is not convenient
since we are missing the varargs methods of clutter_container.

Patch in:

 child_set() - set properties of a child
 add() - add a child and set properties (this is different from
         clutter_container_add()! I think the deviation is
         with avoiding the awkward name add_with_properties()
         which is what might be expected. ClutterContainer
         currently doesn't have a method like this at all.)

The code is written to allow patching into multiple ClutterContainer
classes but for now only StBoxLayout is patched, since it's the only
container we are using where we need to set options as properties.

https://bugzilla.gnome.org/show_bug.cgi?id=595419
2009-10-01 14:41:17 -04:00
83402957bb Add GObject Introspection annotations
Add GObject Introspection annotations to methods where needed, in
particular adding (transfer none) to return values that don't transfer
ownership.

st_texture_cache_get_actor() and st_texture_cache_get_texture()
are annotated as (transfer none) since they return a newly
created *floating* texture.

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:17 -04:00
58325fca76 Import MxEntry, MxLabel, MxClipboard
For now this commit introduces an external dependency on clutter-imcontext.

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:17 -04:00
8b6962f3bf Import MxBoxLayout, MxBoxLayoutChild
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:17 -04:00
c1f91def74 Add css for scrolling
Port bits of Mx's default.css into gnome-shell.css, and use some
hand-rolled .pngs with colors from
http://live.gnome.org/GnomeShell/DesignerPlayground/ExpandedViewMockups

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:17 -04:00
529f74c0e5 Load gnome-shell.css at startup
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:17 -04:00
459a3b18f2 Add a "datadir" property
Will be used to load stylesheets from main.js.

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:16 -04:00
ac2be7f0d1 Remove hardcoded '28' from StScrollView
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:16 -04:00
f6b80d5ed4 Import MxScrollView and dependencies
https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:16 -04:00
d291e568fd Import Mx core as ST
Import the core MxWidget/MxBin and their dependencies; we use the
namespace "St" (Shell Toolkit) because it is the same length as Mx
so enabling easy sharing of code, but makes it clear that this is
a friendly fork and not a literal import.

Based on a patch by Colin Walters <walters@verbum.org>

https://bugzilla.gnome.org/show_bug.cgi?id=591245
2009-10-01 14:41:16 -04:00
2b78d5bd5d Improve support for multihead setups
Fix panel, app switcher, and looking glass to limit themselves to the
primary monitor, and run dialog to limit itself to the monitor
containing the currently-focused window.

The overview is also limited to the primary monitor now (with the
other monitors being blacked out), although the workspaces within the
overview are shaped like the full "screen" (the bounding box of all
monitors). To be fixed later.

https://bugzilla.gnome.org/show_bug.cgi?id=593060
2009-10-01 13:46:03 -04:00
c9d9846759 Add Desktop in Places
Updated by Colin Walters <walters@verbum.org to monitor gconf
and use g_get_user_special_dir.

Signed-off-by: Adel Gadllah <adel.gadllah@gmail.com>
Signed-off-by: Colin Walters <walters@verbum.org>

https://bugzilla.gnome.org/show_bug.cgi?id=596933
2009-10-01 11:53:30 -04:00
6baafaa530 [places] Fix double spacing between actions and devices
https://bugzilla.gnome.org/show_bug.cgi?id=596991
2009-10-01 10:43:57 -04:00
c2af05f753 [AppSwitcher] Allow use of arrow keys in the popup
https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-10-01 10:35:40 -04:00
97df305a6d [AppSwitcher] Drop the line wrapping code.
Also rename _grid into _appsBox as grid is not an appropriate word
anymore for what this box is.

https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-09-30 23:32:36 +02:00
fee385ba35 [AppSwitcher] Do not show the glow for icons in alt+tab dialog.
Unlike icons in the application well, do not show the glow used to
indicate running apps.  It is somewhat redundant here.  These are all
running apps and it is fairly clear from the window list if there are
multiple instances available, according to mccann.

https://bugzilla.gnome.org/show_bug.cgi?id=590563
2009-09-29 18:43:35 +02:00
117 changed files with 22927 additions and 1082 deletions

2
.gitignore vendored
View File

@ -38,5 +38,7 @@ src/gnomeshell-taskpanel
src/gnome-shell
src/test-recorder
src/test-recorder.ogg
src/test-theme
stamp-h1
tests/run-test.sh
xmldocs.make

View File

@ -1,13 +1,22 @@
SUBDIRS = data js src po
SUBDIRS = data js src tests po
EXTRA_DIST = \
.project \
.settings
.settings \
autogen.sh
# These are files checked into Git that we don't want to distribute
DIST_EXCLUDE = \
.gitignore \
gnome-shell.doap \
MAINTAINERS \
tools/build/*
distcheck-hook:
@echo "Checking disted javascript against files in git"
@echo "Checking disted files against files in git"
@failed=false; \
for f in `cd $(srcdir) && git ls-files js` ; do \
exclude=`(for p in $(DIST_EXCLUDE) ; do echo --exclude=$$p ; done)`; \
for f in `cd $(srcdir) && git ls-files $$exclude` ; do \
if ! test -e $(distdir)/$$f ; then \
echo File missing from distribution: $$f ; \
failed=true ; \

View File

@ -1,4 +1,4 @@
AC_INIT(gnome-shell, 2.27.3)
AC_INIT(gnome-shell, 2.28.0)
AC_CONFIG_AUX_DIR(config)
@ -57,6 +57,7 @@ PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugin
gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
gobject-introspection-1.0 >= 0.6.5)
PKG_CHECK_MODULES(TIDY, clutter-1.0)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 libcroco-0.6)
PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
PKG_CHECK_MODULES(TRAY, gtk+-2.0)
@ -128,5 +129,6 @@ AC_OUTPUT([
js/misc/Makefile
js/ui/Makefile
src/Makefile
tests/Makefile
po/Makefile.in
])

View File

@ -12,18 +12,26 @@ gnome-shell.desktop.in: gnome-shell.desktop.in.in
gnome-shell.desktop: gnome-shell.desktop.in
$(AM_V_GEN) sed s/^_// < $< > $@ || rm $@
imagedir = $(pkgdatadir)/images
dist_image_DATA = \
add-workspace.svg \
app-well-glow.png \
back.svg \
close.svg \
close-black.svg \
info.svg \
magnifier.svg \
imagesdir = $(pkgdatadir)/images
dist_images_DATA = \
add-workspace.svg \
app-well-glow.png \
back.svg \
close.svg \
close-black.svg \
info.svg \
magnifier.svg \
remove-workspace.svg
themedir = $(pkgdatadir)/theme
dist_theme_DATA = \
theme/gnome-shell.css \
theme/scroll-button-down.png \
theme/scroll-button-down-hover.png \
theme/scroll-button-up.png \
theme/scroll-button-up-hover.png \
theme/scroll-vhandle.png
schemadir = @GCONF_SCHEMA_FILE_DIR@
schema_DATA = gnome-shell.schemas

176
data/theme/gnome-shell.css Normal file
View File

@ -0,0 +1,176 @@
/* Copyright 2009, Red Hat, Inc.
*
* Portions adapted from Mx's data/style/default.css
* Copyright 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
StScrollBar
{
background-color: #354761;
padding: 0px;
}
StScrollView
{
scrollbar-width: 16px;
scrollbar-height: 16px;
}
StButton#up-stepper
{
border-image: url("scroll-button-up.png") 5;
}
StButton#up-stepper:hover,
StButton#up-stepper:active
{
border-image: url("scroll-button-up-hover.png") 5;
}
StButton#down-stepper
{
border-image: url("scroll-button-down.png") 5;
}
StButton#down-stepper:hover,
StButton#down-stepper:active
{
border-image: url("scroll-button-down-hover.png") 5;
}
StScrollBar StButton#vhandle
{
border-image: url("scroll-vhandle.png") 5;
}
StScrollBar StButton#vhandle:hover
{
border-image: url("scroll-vhandle.png") 5;
}
/* Panel */
.panel-button {
padding: 4px 12px 3px;
border-radius: 5px;
font: 16px sans-serif;
color: white;
}
.panel-button:active, .panel-button:checked {
background-color: #314a6c;
}
/* LookingGlass */
#LookingGlassDialog
{
background-color: rgba(0,0,0,0.85);
spacing: 4px;
padding: 4px;
border: 1px solid rgba(0,0,172,0.85);
border-radius: 4px;
color: #88ff66;
}
#LookingGlassDialog > #Toolbar
{
border: 1px solid grey;
border-radius: 4px;
}
#LookingGlassDialog StLabel
{
color: #88ff66;
}
#LookingGlassDialog StEntry
{
color: #88ff66;
}
#LookingGlassDialog StBoxLayout#EvalBox
{
padding: 4px;
spacing: 4px;
}
/* Calendar popup */
#calendarPopup {
border-radius: 5px;
background: rgba(0,0,0,0.9);
border: 1px solid rgba(128,128,128,0.45);
color: white;
}
#calendarPopup .calendar {
padding: 10px;
}
.calendar {
spacing-rows: 5px;
spacing-columns: 3px;
}
.calendar-change-month {
padding: 2px;
}
.calendar-change-month:hover {
background: #314a6c;
border-radius: 5px;
}
.calendar-change-month:active {
background: #213050;
border-radius: 5px;
}
.calendar-day {
padding: 1px 2px;
}
.calendar-today {
font-weight: bold;
background: #ffffff;
color: black;
border-radius: 5px;
}
.calendar-other-month-day {
color: #cccccc;
}
/* App Switcher */
.switcher-list {
background: rgba(0,0,0,0.8);
border: 1px solid rgba(128,128,128,0.40);
border-radius: 8px;
padding: 18px;
}
.switcher-list .item-box {
padding: 8px;
border-radius: 4px;
}
.switcher-list .selected-item-box {
padding: 8px;
border-radius: 4px;
background: rgba(255,255,255,0.33);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 B

View File

@ -1,4 +1,5 @@
jsmiscdir = $(pkgdatadir)/js/misc
dist_jsmisc_DATA = \
docInfo.js
docInfo.js \
format.js

44
js/misc/format.js Normal file
View File

@ -0,0 +1,44 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* This function is intended to extend the String object and provide
* an String.format API for string formatting.
* It has to be set up using String.prototype.format = Format.format;
* Usage:
* "somestring %s %d".format('hello', 5);
* It supports %s, %d and %f, for %f it also support precisions like
* "%.2f".format(1.526)
*/
function format() {
let str = this;
let i = 0;
let args = arguments;
return str.replace(/%(?:\.([0-9]+))?(.)/g, function (str, precisionGroup, genericGroup) {
if (precisionGroup != '' && genericGroup != 'f')
throw new Error("Precision can only be specified for 'f'");
switch (genericGroup) {
case '%':
return '%';
break;
case 's':
return args[i++].toString();
break;
case 'd':
return parseInt(args[i++]);
break;
case 'f':
if (precisionGroup == '')
return parseFloat(args[i++]);
else
return parseFloat(args[i++]).toFixed(parseInt(precisionGroup));
break;
default:
throw new Error('Unsupported conversion character %' + genericGroup);
}
return ""; // Suppress warning
});
}

View File

@ -5,10 +5,12 @@ dist_jsui_DATA = \
appDisplay.js \
appIcon.js \
button.js \
calendar.js \
chrome.js \
dash.js \
dnd.js \
docDisplay.js \
environment.js \
genericDisplay.js \
lightbox.js \
link.js \

View File

@ -4,84 +4,61 @@ const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const AppIcon = imports.ui.appIcon;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const POPUP_BG_COLOR = new Clutter.Color();
POPUP_BG_COLOR.from_pixel(0x00000080);
const POPUP_APPICON_BORDER_COLOR = new Clutter.Color();
POPUP_APPICON_BORDER_COLOR.from_pixel(0xffffffff);
const POPUP_ARROW_COLOR = new Clutter.Color();
POPUP_ARROW_COLOR.from_pixel(0xffffffff);
const TRANSPARENT_COLOR = new Clutter.Color();
TRANSPARENT_COLOR.from_pixel(0x00000000);
const POPUP_SEPARATOR_COLOR = new Clutter.Color();
POPUP_SEPARATOR_COLOR.from_pixel(0x80808066);
const POPUP_GRID_SPACING = 8;
const POPUP_ICON_SIZE = 48;
const POPUP_NUM_COLUMNS = 5;
const POPUP_APPICON_SIZE = 96;
const POPUP_LIST_SPACING = 8;
const POPUP_POINTER_SELECTION_THRESHOLD = 3;
const THUMBNAIL_SIZE = 256;
const THUMBNAIL_POPUP_TIME = 1000; // milliseconds
const HOVER_TIME = 500; // milliseconds
function mod(a, b) {
return (a + b) % b;
}
function AltTabPopup() {
this._init();
}
AltTabPopup.prototype = {
_init : function() {
this.actor = new Big.Box({ background_color : POPUP_BG_COLOR,
corner_radius: POPUP_GRID_SPACING,
padding: POPUP_GRID_SPACING,
spacing: POPUP_GRID_SPACING,
orientation: Big.BoxOrientation.VERTICAL,
reactive: true });
this.actor = new Clutter.Group({ reactive: true,
x: 0,
y: 0,
width: global.screen_width,
height: global.screen_height });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
// Icon grid. TODO: Investigate Nbtk.Grid once that lands. Currently
// just implemented using a chain of Big.Box.
this._grid = new Big.Box({ spacing: POPUP_GRID_SPACING,
orientation: Big.BoxOrientation.VERTICAL });
let gcenterbox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x_align: Big.BoxAlignment.CENTER });
gcenterbox.append(this._grid, Big.BoxPackFlags.NONE);
this.actor.append(gcenterbox, Big.BoxPackFlags.NONE);
this._icons = [];
this._currentWindows = [];
this._haveModal = false;
this._selected = 0;
this._highlightedWindow = null;
this._toplevels = global.window_group.get_children();
this._currentApp = 0;
this._currentWindows = [];
this._thumbnailTimeoutId = 0;
global.stage.add_actor(this.actor);
},
_addIcon : function(appIcon) {
appIcon.connect('activate', Lang.bind(this, this._appClicked));
appIcon.connect('activate-window', Lang.bind(this, this._windowClicked));
appIcon.connect('highlight-window', Lang.bind(this, this._windowHovered));
appIcon.connect('menu-popped-up', Lang.bind(this, this._menuPoppedUp));
appIcon.connect('menu-popped-down', Lang.bind(this, this._menuPoppedDown));
appIcon.actor.connect('enter-event', Lang.bind(this, this._iconEntered));
// FIXME?
appIcon.actor.border = 2;
appIcon.highlight_border_color = POPUP_APPICON_BORDER_COLOR;
this._icons.push(appIcon);
this._currentWindows.push(appIcon.windows[0]);
// Add it to the grid
if (!this._gridRow || this._gridRow.get_children().length == POPUP_NUM_COLUMNS) {
this._gridRow = new Big.Box({ spacing: POPUP_GRID_SPACING,
orientation: Big.BoxOrientation.HORIZONTAL });
this._grid.append(this._gridRow, Big.BoxPackFlags.NONE);
}
this._gridRow.append(appIcon.actor, Big.BoxPackFlags.NONE);
},
show : function(initialSelection) {
show : function(backward) {
let appMonitor = Shell.AppMonitor.get_default();
let apps = appMonitor.get_running_apps ("");
@ -99,32 +76,52 @@ AltTabPopup.prototype = {
this._mouseActive = false;
this._mouseMovement = 0;
// Contruct the AppIcons, sort by time, add to the popup
let icons = [];
for (let i = 0; i < apps.length; i++)
icons.push(new AppIcon.AppIcon(apps[i], AppIcon.MenuType.BELOW));
icons.sort(Lang.bind(this, this._sortAppIcon));
for (let i = 0; i < icons.length; i++)
this._addIcon(icons[i]);
this._appSwitcher = new AppSwitcher(apps);
this.actor.add_actor(this._appSwitcher.actor);
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
this._appSwitcher.connect('item-hovered', Lang.bind(this, this._appHovered));
// Need to specify explicit width and height because the
// window_group may not actually cover the whole screen
this._lightbox = new Lightbox.Lightbox(global.window_group,
global.screen_width,
global.screen_height);
let primary = global.get_primary_monitor();
this._appSwitcher.actor.x = primary.x + Math.floor((primary.width - this._appSwitcher.actor.width) / 2);
this._appSwitcher.actor.y = primary.y + Math.floor((primary.height - this._appSwitcher.actor.height) / 2);
this.actor.show_all();
this.actor.x = Math.floor((global.screen_width - this.actor.width) / 2);
this.actor.y = Math.floor((global.screen_height - this.actor.height) / 2);
this._appIcons = this._appSwitcher.icons;
this._updateSelection(initialSelection);
// _currentWindows give the index of the selected window for
// each app; they all start at 0.
this._currentWindows = this._appIcons.map(function (app) { return 0; });
// Make the initial selection
if (this._appIcons.length == 1) {
if (!backward && this._appIcons[0].windows.length > 1) {
// For compatibility with the multi-app case below
this._select(0, 1);
} else
this._select(0);
} else if (backward) {
this._select(this._appIcons.length - 1);
} else {
if (this._appIcons[0].windows.length > 1) {
let curAppNextWindow = this._appIcons[0].windows[1];
let nextAppWindow = this._appIcons[1].windows[0];
// If the next window of the current app is more-recently-used
// than the first window of the next app, then select it.
if (curAppNextWindow.get_workspace() == global.screen.get_active_workspace() &&
curAppNextWindow.get_user_time() > nextAppWindow.get_user_time())
this._select(0, 1);
else
this._select(1);
} else
this._select(1);
}
// There's a race condition; if the user released Alt before
// we got the grab, then we won't be notified. (See
// https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
// details.) So we check now. (Have to do this after calling
// _updateSelection.)
let [screen, x, y, mods] = Gdk.Display.get_default().get_pointer();
// details.) So we check now. (Have to do this after updating
// selection.)
let mods = global.get_modifier_keys();
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
this._finish();
return false;
@ -133,6 +130,502 @@ AltTabPopup.prototype = {
return true;
},
_nextApp : function() {
return mod(this._currentApp + 1, this._appIcons.length);
},
_previousApp : function() {
return mod(this._currentApp - 1, this._appIcons.length);
},
_nextWindow : function() {
return mod(this._currentWindows[this._currentApp] + 1,
this._appIcons[this._currentApp].windows.length);
},
_previousWindow : function() {
return mod(this._currentWindows[this._currentApp] - 1,
this._appIcons[this._currentApp].windows.length);
},
_keyPressEvent : function(actor, event) {
let keysym = event.get_key_symbol();
let shift = (Shell.get_event_state(event) & Clutter.ModifierType.SHIFT_MASK);
// The WASD stuff is for debugging in Xephyr, where the arrow
// keys aren't mapped correctly
if (keysym == Clutter.Tab)
this._select(shift ? this._previousApp() : this._nextApp());
else if (keysym == Clutter.grave)
this._select(this._currentApp, shift ? this._previousWindow() : this._nextWindow());
else if (keysym == Clutter.Escape)
this.destroy();
else if (this._thumbnails) {
if (keysym == Clutter.Left || keysym == Clutter.a)
this._select(this._currentApp, this._previousWindow());
else if (keysym == Clutter.Right || keysym == Clutter.d)
this._select(this._currentApp, this._nextWindow());
else if (keysym == Clutter.Up || keysym == Clutter.w)
this._select(this._currentApp, null, true);
} else {
if (keysym == Clutter.Left || keysym == Clutter.a)
this._select(this._previousApp());
else if (keysym == Clutter.Right || keysym == Clutter.d)
this._select(this._nextApp());
else if (keysym == Clutter.Down || keysym == Clutter.s)
this._select(this._currentApp, this._currentWindows[this._currentApp]);
}
return true;
},
_keyReleaseEvent : function(actor, event) {
let keysym = event.get_key_symbol();
if (keysym == Clutter.Alt_L || keysym == Clutter.Alt_R)
this._finish();
return true;
},
_appActivated : function(appSwitcher, n) {
Main.activateWindow(this._appIcons[n].windows[this._currentWindows[n]]);
this.destroy();
},
_appHovered : function(appSwitcher, n) {
if (!this._mouseActive)
return;
this._select(n, this._currentWindows[n]);
},
_windowActivated : function(thumbnailList, n) {
Main.activateWindow(this._appIcons[this._currentApp].windows[n]);
this.destroy();
},
_windowHovered : function(thumbnailList, n) {
if (!this._mouseActive)
return;
this._select(this._currentApp, n);
},
_mouseMoved : function(actor, event) {
if (++this._mouseMovement < POPUP_POINTER_SELECTION_THRESHOLD)
return;
this.actor.disconnect(this._motionEventId);
this._mouseActive = true;
this._appSwitcher.checkHover();
if (this._thumbnails)
this._thumbnails.checkHover();
},
_finish : function() {
let app = this._appIcons[this._currentApp];
let window = app.windows[this._currentWindows[this._currentApp]];
Main.activateWindow(window);
this.destroy();
},
destroy : function() {
this.actor.destroy();
},
_onDestroy : function() {
if (this._haveModal)
Main.popModal(this.actor);
if (this._keyPressEventId)
global.stage.disconnect(this._keyPressEventId);
if (this._keyReleaseEventId)
global.stage.disconnect(this._keyReleaseEventId);
if (this._thumbnailTimeoutId != 0)
Mainloop.source_remove(this._thumbnailTimeoutId);
},
_select : function(app, window, noTimeout) {
if ((app != this._currentApp || !window) && this._thumbnails) {
this._thumbnails.actor.destroy();
this._thumbnails = null;
this._appSwitcher.showArrow(-1);
}
if (this._thumbnailTimeoutId != 0) {
Mainloop.source_remove(this._thumbnailTimeoutId);
this._thumbnailTimeoutId = 0;
}
this._currentApp = app;
if (window != null) {
this._appSwitcher.highlight(-1);
this._appSwitcher.showArrow(app);
} else {
this._appSwitcher.highlight(app);
if (this._appIcons[this._currentApp].windows.length > 1)
this._appSwitcher.showArrow(app);
}
if (window != null) {
if (!this._thumbnails)
this._createThumbnails();
this._currentWindows[this._currentApp] = window;
this._thumbnails.highlight(window);
} else if (this._appIcons[this._currentApp].windows.length > 1 &&
!noTimeout) {
this._thumbnailTimeoutId = Mainloop.timeout_add (
THUMBNAIL_POPUP_TIME,
Lang.bind(this, function () {
this._select(this._currentApp,
this._currentWindows[this._currentApp]);
return false;
}));
}
},
_createThumbnails : function() {
this._thumbnails = new ThumbnailList (this._appIcons[this._currentApp].windows);
this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated));
this._thumbnails.connect('item-hovered', Lang.bind(this, this._windowHovered));
this.actor.add_actor(this._thumbnails.actor);
let thumbnailCenter;
if (this._thumbnails.actor.width < this._appSwitcher.actor.width) {
// Center the thumbnails under the corresponding AppIcon.
// If this is being called when the switcher is first
// being brought up, then nothing will have been assigned
// an allocation yet, and the get_transformed_position()
// call will return 0,0.
// (http://bugzilla.openedhand.com/show_bug.cgi?id=1115).
// Calling clutter_actor_get_allocation_box() would force
// it to properly allocate itself, but we can't call that
// because it has an out-caller-allocates arg. So we use
// clutter_stage_get_actor_at_pos(), which will force a
// reallocation as a side effect.
global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, 0, 0);
let icon = this._appIcons[this._currentApp].actor;
let [stageX, stageY] = icon.get_transformed_position();
thumbnailCenter = stageX + icon.width / 2;
} else {
// Center the thumbnails on the monitor
let primary = global.get_primary_monitor();
thumbnailCenter = primary.x + primary.width / 2;
}
this._thumbnails.actor.x = Math.floor(thumbnailCenter - this._thumbnails.actor.width / 2);
this._thumbnails.actor.y = this._appSwitcher.actor.y + this._appSwitcher.actor.height + POPUP_LIST_SPACING;
}
};
function SwitcherList(squareItems) {
this._init(squareItems);
}
SwitcherList.prototype = {
_init : function(squareItems) {
this.actor = new St.Bin({ style_class: 'switcher-list' });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
// Here we use a GenericContainer so that we can force all the
// children except the separator to have the same width.
this._list = new Shell.GenericContainer();
this._list.spacing = POPUP_LIST_SPACING;
this._list.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this._list.connect('allocate', Lang.bind(this, this._allocate));
this.actor.add_actor(this._list);
this._items = [];
this._highlighted = -1;
this._separator = null;
this._squareItems = squareItems;
this._hoverTimeout = 0;
},
_onDestroy: function() {
if (this._hoverTimeout != 0) {
Mainloop.source_remove(this._hoverTimeout);
this._hoverTimeout = 0;
}
},
addItem : function(item) {
let box = new St.Bin({ style_class: 'item-box' });
let bbox;
if (item instanceof Shell.ButtonBox)
bbox = item;
else {
bbox = new Shell.ButtonBox({ reactive: true });
bbox.append(item, Big.BoxPackFlags.NONE);
}
box.add_actor(bbox);
this._list.add_actor(box);
let n = this._items.length;
bbox.connect('activate', Lang.bind(this, function () {
this._itemActivated(n);
}));
bbox.connect('notify::hover', Lang.bind(this, function () {
this._hoverChanged(bbox, n);
}));
this._items.push(box);
},
addSeparator: function () {
// FIXME: make this work with StWidgets and CSS
let box = new Big.Box({ padding_top: 2, padding_bottom: 2 });
box.append(new Clutter.Rectangle({ width: 1,
color: POPUP_SEPARATOR_COLOR }),
Big.BoxPackFlags.EXPAND);
this._separator = box;
this._list.add_actor(box);
},
highlight: function(index) {
if (this._highlighted != -1)
this._items[this._highlighted].style_class = 'item-box';
this._highlighted = index;
if (this._highlighted != -1)
this._items[this._highlighted].style_class = 'selected-item-box';
},
// Used after the mouse movement exceeds the threshold, to check
// if it's already hovering over an icon
checkHover: function() {
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].get_child().hover) {
this._hoverChanged(this._items[i].get_child(), i);
return;
}
}
},
_itemActivated: function(n) {
this.emit('item-activated', n);
},
_hoverChanged: function(box, n) {
if (this._hoverTimeout != 0) {
Mainloop.source_remove(this._hoverTimeout);
this._hoverTimeout = 0;
}
if (box.hover) {
this._hoverTimeout = Mainloop.timeout_add(
HOVER_TIME,
Lang.bind (this, function () {
this._itemHovered(n);
}));
}
},
_itemHovered: function(n) {
this.emit('item-hovered', n);
},
_maxChildWidth: function (forHeight) {
let maxChildMin = 0;
let maxChildNat = 0;
for (let i = 0; i < this._items.length; i++) {
let [childMin, childNat] = this._items[i].get_preferred_width(forHeight);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
if (this._squareItems) {
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
}
}
return [maxChildMin, maxChildNat];
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
let separatorWidth = 0;
if (this._separator) {
let [sepMin, sepNat] = this._separator.get_preferred_width(forHeight);
separatorWidth = sepNat + this._list.spacing;
}
let totalSpacing = this._list.spacing * (this._items.length - 1);
alloc.min_size = this._items.length * maxChildMin + separatorWidth + totalSpacing;
alloc.nat_size = this._items.length * maxChildNat + separatorWidth + totalSpacing;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let maxChildMin = 0;
let maxChildNat = 0;
for (let i = 0; i < this._items.length; i++) {
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
}
if (this._squareItems) {
let [childMin, childNat] = this._maxChildWidth(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
}
alloc.min_size = maxChildMin;
alloc.nat_size = maxChildNat;
},
_allocate: function (actor, box, flags) {
let childHeight = box.y2 - box.y1;
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
let totalSpacing = this._list.spacing * (this._items.length - 1);
let separatorWidth = 0;
if (this._separator) {
let [sepMin, sepNat] = this._separator.get_preferred_width(childHeight);
separatorWidth = sepNat;
totalSpacing += this._list.spacing;
}
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing - separatorWidth) / this._items.length);
let x = 0;
let children = this._list.get_children();
let childBox = new Clutter.ActorBox();
for (let i = 0; i < children.length; i++) {
if (this._items.indexOf(children[i]) != -1) {
let [childMin, childNat] = children[i].get_preferred_height(childWidth);
let vSpacing = (childHeight - childNat) / 2;
childBox.x1 = x;
childBox.y1 = vSpacing;
childBox.x2 = x + childWidth;
childBox.y2 = childBox.y1 + childNat;
children[i].allocate(childBox, flags);
x += this._list.spacing + childWidth;
} else if (children[i] == this._separator) {
// We want the separator to be more compact than the rest.
childBox.x1 = x;
childBox.y1 = 0;
childBox.x2 = x + separatorWidth;
childBox.y2 = childHeight;
children[i].allocate(childBox, flags);
x += this._list.spacing + separatorWidth;
} else {
// Something else, eg, AppSwitcher's arrows;
// we don't allocate it.
}
}
}
};
Signals.addSignalMethods(SwitcherList.prototype);
function AppSwitcher(apps) {
this._init(apps);
}
AppSwitcher.prototype = {
__proto__ : SwitcherList.prototype,
_init : function(apps) {
SwitcherList.prototype._init.call(this, true);
// Construct the AppIcons, sort by time, add to the popup
let activeWorkspace = global.screen.get_active_workspace();
let workspaceIcons = [];
let otherIcons = [];
for (let i = 0; i < apps.length; i++) {
let appIcon = new AppIcon.AppIcon({ appInfo: apps[i],
size: POPUP_APPICON_SIZE });
if (this._hasWindowsOnWorkspace(appIcon, activeWorkspace))
workspaceIcons.push(appIcon);
else
otherIcons.push(appIcon);
}
workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
otherIcons.sort(Lang.bind(this, this._sortAppIcon));
this.icons = [];
this._arrows = [];
for (let i = 0; i < workspaceIcons.length; i++)
this._addIcon(workspaceIcons[i]);
if (workspaceIcons.length > 0 && otherIcons.length > 0)
this.addSeparator();
for (let i = 0; i < otherIcons.length; i++)
this._addIcon(otherIcons[i]);
this._shownArrow = -1;
},
_allocate: function (actor, box, flags) {
// Allocate the main list items
SwitcherList.prototype._allocate.call(this, actor, box, flags);
let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
let arrowWidth = arrowHeight * 2;
// Now allocate each arrow underneath its item
let childBox = new Clutter.ActorBox();
for (let i = 0; i < this._items.length; i++) {
let itemBox = this._items[i].allocation;
childBox.x1 = Math.floor(itemBox.x1 + (itemBox.x2 - itemBox.x1 - arrowWidth) / 2);
childBox.x2 = childBox.x1 + arrowWidth;
childBox.y1 = itemBox.y2 + arrowHeight;
childBox.y2 = childBox.y1 + arrowHeight;
this._arrows[i].allocate(childBox, flags);
}
},
showArrow : function(n) {
if (this._shownArrow != -1)
this._arrows[this._shownArrow].hide();
this._shownArrow = n;
if (this._shownArrow != -1)
this._arrows[this._shownArrow].show();
},
_addIcon : function(appIcon) {
this.icons.push(appIcon);
this.addItem(appIcon.actor);
let arrow = new Shell.DrawingArea();
arrow.connect('redraw', Lang.bind(this,
function (area, texture) {
Shell.draw_box_pointer(texture, Shell.PointerDirection.DOWN,
TRANSPARENT_COLOR,
POPUP_ARROW_COLOR);
}));
this._list.add_actor(arrow);
this._arrows.push(arrow);
arrow.hide();
},
_hasWindowsOnWorkspace: function(appIcon, workspace) {
for (let i = 0; i < appIcon.windows.length; i++) {
if (appIcon.windows[i].get_workspace() == workspace)
return true;
}
return false;
},
_hasVisibleWindows : function(appIcon) {
for (let i = 0; i < appIcon.windows.length; i++) {
if (appIcon.windows[i].showing_on_its_workspace())
@ -155,133 +648,32 @@ AltTabPopup.prototype = {
return (appIcon2.windows[0].get_user_time() -
appIcon1.windows[0].get_user_time());
}
},
_keyPressEvent : function(actor, event) {
let keysym = event.get_key_symbol();
let backwards = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
if (keysym == Clutter.Tab)
this._updateSelection(backwards ? -1 : 1);
else if (keysym == Clutter.grave)
this._updateWindowSelection(backwards ? -1 : 1);
else if (keysym == Clutter.Escape)
this.destroy();
return true;
},
_keyReleaseEvent : function(actor, event) {
let keysym = event.get_key_symbol();
if (keysym == Clutter.Alt_L || keysym == Clutter.Alt_R)
this._finish();
return true;
},
_appClicked : function(icon) {
Main.activateWindow(icon.windows[0]);
this.destroy();
},
_windowClicked : function(icon, window) {
if (window)
Main.activateWindow(window);
this.destroy();
},
_windowHovered : function(icon, window) {
if (window)
this._highlightWindow(window);
},
_mouseMoved : function(actor, event) {
if (++this._mouseMovement < POPUP_POINTER_SELECTION_THRESHOLD)
return;
this.actor.disconnect(this._motionEventId);
this._mouseActive = true;
actor = event.get_source();
while (actor) {
if (actor._delegate instanceof AppIcon.AppIcon) {
this._iconEntered(actor, event);
return;
}
actor = actor.get_parent();
}
},
_iconEntered : function(actor, event) {
let index = this._icons.indexOf(actor._delegate);
if (this._mouseActive)
this._updateSelection(index - this._selected);
},
_finish : function() {
if (this._highlightedWindow)
Main.activateWindow(this._highlightedWindow);
this.destroy();
},
destroy : function() {
this.actor.destroy();
},
_onDestroy : function() {
if (this._haveModal)
Main.popModal(this.actor);
if (this._lightbox)
this._lightbox.destroy();
if (this._keyPressEventId)
global.stage.disconnect(this._keyPressEventId);
if (this._keyReleaseEventId)
global.stage.disconnect(this._keyReleaseEventId);
},
_updateSelection : function(delta) {
this._icons[this._selected].setHighlight(false);
if (delta != 0 && this._selectedMenu)
this._selectedMenu.popdown();
this._selected = (this._selected + this._icons.length + delta) % this._icons.length;
this._icons[this._selected].setHighlight(true);
this._highlightWindow(this._currentWindows[this._selected]);
},
_menuPoppedUp : function(icon, menu) {
this._selectedMenu = menu;
},
_menuPoppedDown : function(icon, menu) {
this._selectedMenu = null;
},
_updateWindowSelection : function(delta) {
let icon = this._icons[this._selected];
if (!this._selectedMenu)
icon.popupMenu();
if (!this._selectedMenu)
return;
let next = 0;
for (let i = 0; i < icon.windows.length; i++) {
if (icon.windows[i] == this._highlightedWindow) {
next = (i + icon.windows.length + delta) % icon.windows.length;
break;
}
}
this._selectedMenu.selectWindow(icon.windows[next]);
},
_highlightWindow : function(metaWin) {
this._highlightedWindow = metaWin;
this._currentWindows[this._selected] = metaWin;
this._lightbox.highlight(this._highlightedWindow.get_compositor_private());
}
};
function ThumbnailList(windows) {
this._init(windows);
}
ThumbnailList.prototype = {
__proto__ : SwitcherList.prototype,
_init : function(windows) {
SwitcherList.prototype._init.call(this);
for (let i = 0; i < windows.length; i++) {
let mutterWindow = windows[i].get_compositor_private();
let windowTexture = mutterWindow.get_texture ();
let [width, height] = windowTexture.get_size();
let scale = Math.min(1.0, THUMBNAIL_SIZE / width, THUMBNAIL_SIZE / height);
let clone = new Clutter.Clone ({ source: windowTexture,
reactive: true,
width: width * scale,
height: height * scale });
let box = new Big.Box({ padding: AppIcon.APPICON_BORDER_WIDTH + AppIcon.APPICON_PADDING });
box.append(clone, Big.BoxPackFlags.NONE);
this.addItem(box);
}
}
};

View File

@ -177,31 +177,21 @@ AppDisplay.prototype = {
this._menus = [];
this._menuDisplays = [];
// map<itemId, array of category names>
this._appCategories = {};
// map<search term, map<appId, true>>
// We use a map of appIds instead of an array to ensure that we don't have duplicates and for easier lookup.
this._menuSearchAppMatches = {};
this._appMonitor = Shell.AppMonitor.get_default();
this._appSystem = Shell.AppSystem.get_default();
this._appsStale = true;
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
this._appsStale = true;
this._redisplay(0);
this._redisplayMenus();
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
}));
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
this._redisplay(0);
this._appsStale = true;
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
}));
this._appMonitor.connect('app-added', Lang.bind(this, function(monitor) {
this._redisplay(0);
}));
this._appMonitor.connect('app-removed', Lang.bind(this, function(monitor) {
this._redisplay(0);
}));
// Load the apps now so it doesn't slow down the first
// transition into the Overview
this._refreshCache();
this._focusInMenus = true;
this._activeMenuIndex = -1;
@ -210,7 +200,6 @@ AppDisplay.prototype = {
this._menuDisplay = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: MENU_SPACING
});
this._redisplayMenus();
this.connect('expanded', Lang.bind(this, function (self) {
this._filterReset();
@ -253,6 +242,39 @@ AppDisplay.prototype = {
return true;
},
setSearch: function(text) {
let lowertext = text.toLowerCase();
if (lowertext == this._search)
return;
// We prepare menu matches up-front, so that we don't
// need to go over all menu items for each application
// and then get all applications for a matching menu
// to see if a particular application passed to
// _isInfoMatching() is a match.
let terms = lowertext.split(/\s+/);
this._menuSearchAppMatches = {};
for (let i = 0; i < terms.length; i++) {
let term = terms[i];
this._menuSearchAppMatches[term] = {};
for (let j = 0; j < this._menus.length; j++) {
let menuItem = this._menus[j];
// Match only on the beginning of the words in category names,
// because otherwise it introduces unnecessary noise in the results.
if (menuItem.name.toLowerCase().indexOf(term) == 0 ||
menuItem.name.toLowerCase().indexOf(" " + term) > 0) {
let menuApps = this._appSystem.get_applications_for_menu(menuItem.id);
for (let k = 0; k < menuApps.length; k++) {
let menuApp = menuApps[k];
this._menuSearchAppMatches[term][menuApp.get_id()] = true;
}
}
}
}
GenericDisplay.GenericDisplay.prototype.setSearch.call(this, text);
},
// Protected overrides
_filterActive: function() {
@ -303,7 +325,7 @@ AppDisplay.prototype = {
this._activeMenuApps = this._appSystem.get_applications_for_menu(id);
}
}
this._redisplay(true);
this._redisplay(GenericDisplay.RedisplayFlags.FULL);
}));
this._menuDisplay.append(display.actor, 0);
},
@ -333,7 +355,6 @@ AppDisplay.prototype = {
if (!this._appsStale)
return true;
this._allItems = {};
this._appCategories = {};
if (this._showPrefs) {
// Get the desktop file ids for settings/preferences.
@ -361,6 +382,7 @@ AppDisplay.prototype = {
this._addApp(app);
}
}
this._redisplayMenus();
}
this._appsStale = false;
@ -383,7 +405,7 @@ AppDisplay.prototype = {
},
// Checks if the item info can be a match for the search string by checking
// the name, description, execution command, and categories for the application.
// the name, description, execution command, and category for the application.
// Item info is expected to be Shell.AppInfo.
// Returns a boolean flag indicating if itemInfo is a match.
_isInfoMatching : function(itemInfo, search) {
@ -395,10 +417,10 @@ AppDisplay.prototype = {
if (this._activeMenu == null || search != "")
return this._isInfoMatchingSearch(itemInfo, search);
else
return this._isInfoMatchingMenu(itemInfo, search);
return this._isInfoMatchingMenu(itemInfo);
},
_isInfoMatchingMenu : function(itemInfo, search) {
_isInfoMatchingMenu: function(itemInfo) {
let id = itemInfo.get_id();
for (let i = 0; i < this._activeMenuApps.length; i++) {
let activeApp = this._activeMenuApps[i];
@ -436,11 +458,11 @@ AppDisplay.prototype = {
return true;
}
let categories = itemInfo.get_categories();
for (let i = 0; i < categories.length; i++) {
let category = fold(categories[i]);
if (category.indexOf(search) >= 0)
if (this._menuSearchAppMatches[search]) {
if (this._menuSearchAppMatches[search].hasOwnProperty(itemInfo.get_id()))
return true;
} else {
log("Missing an entry for search term " + search + " in this._menuSearchAppMatches");
}
return false;
@ -461,8 +483,10 @@ function BaseWellItem(appInfo, isFavorite, hasMenu) {
BaseWellItem.prototype = {
__proto__: AppIcon.AppIcon.prototype,
_init: function(appInfo, isFavorite, hasMenu) {
AppIcon.AppIcon.prototype._init.call(this, appInfo, hasMenu ? AppIcon.MenuType.ON_RIGHT : AppIcon.MenuType.NONE);
_init: function(appInfo, isFavorite) {
AppIcon.AppIcon.prototype._init.call(this, { appInfo: appInfo,
menuType: AppIcon.MenuType.ON_RIGHT,
glow: true });
this.isFavorite = isFavorite;
@ -521,7 +545,7 @@ RunningWellItem.prototype = {
__proto__: BaseWellItem.prototype,
_init: function(appInfo, isFavorite) {
BaseWellItem.prototype._init.call(this, appInfo, isFavorite, true);
BaseWellItem.prototype._init.call(this, appInfo, isFavorite);
this._dragStartX = 0;
this._dragStartY = 0;
@ -530,7 +554,7 @@ RunningWellItem.prototype = {
},
_onActivate: function (actor, event) {
let modifiers = event.get_state();
let modifiers = Shell.get_event_state(event);
if (modifiers & Clutter.ModifierType.CONTROL_MASK) {
this.appInfo.launch();
@ -594,6 +618,12 @@ InactiveWellItem.prototype = {
return true;
}
return false;
},
menuPoppedUp: function() {
},
menuPoppedDown: function() {
}
};
@ -661,10 +691,8 @@ WellGrid.prototype = {
childBox.y2 = childBox.y1 + itemHeight;
children[i].allocate(childBox, flags);
let atSeparator = (i == this._separatorIndex - 1);
columnIndex++;
if (columnIndex == columns || atSeparator) {
if (columnIndex == columns) {
columnIndex = 0;
}
@ -674,27 +702,7 @@ WellGrid.prototype = {
} else {
x += itemWidth;
}
if (atSeparator) {
y += separatorNatural + WELL_ITEM_VSPACING;
}
}
let separatorRowIndex = Math.ceil(this._separatorIndex / columns);
/* Allocate the separator */
let childBox = new Clutter.ActorBox();
childBox.x1 = box.x1;
childBox.y1 = (itemHeight + WELL_ITEM_VSPACING) * separatorRowIndex;
this._cachedSeparatorY = childBox.y1;
childBox.x2 = box.x2;
childBox.y2 = childBox.y1+separatorNatural;
this._separator.allocate(childBox, flags);
},
setSeparatorIndex: function (index) {
this._separatorIndex = index;
this.actor.queue_relayout();
},
removeAll: function () {
@ -702,11 +710,6 @@ WellGrid.prototype = {
for (let i = 0; i < itemChildren.length; i++) {
itemChildren[i].destroy();
}
this._separatorIndex = 0;
},
isBeforeSeparator: function(x, y) {
return y < this._cachedSeparatorY;
},
_getItemChildren: function () {
@ -724,7 +727,10 @@ WellGrid.prototype = {
return [0, WELL_DEFAULT_COLUMNS, 0, 0];
let nColumns = 0;
let usedWidth = 0;
if (forWidth < 0) {
// Big.Box will allocate us at 0x0 if we are not visible; this is probably a
// Big.Box bug but it can't be fixed because if children are skipped in allocate()
// Clutter gets confused (see http://bugzilla.openedhand.com/show_bug.cgi?id=1831)
if (forWidth <= 0) {
nColumns = WELL_DEFAULT_COLUMNS;
} else {
while (nColumns < WELL_DEFAULT_COLUMNS &&
@ -746,11 +752,10 @@ WellGrid.prototype = {
let minWidth = itemMinWidth * nColumns;
let lastColumnIndex = nColumns - 1;
let separatorColumns = lastColumnIndex - ((lastColumnIndex + this._separatorIndex) % nColumns);
let rows = Math.ceil((children.length + separatorColumns) / nColumns);
let rows = Math.ceil(children.length / nColumns);
let itemWidth;
if (forWidth < 0) {
if (forWidth <= 0) {
itemWidth = itemNaturalWidth;
} else {
itemWidth = Math.floor(forWidth / nColumns);
@ -794,6 +799,9 @@ AppWell.prototype = {
x_align: Big.BoxAlignment.CENTER });
this.actor._delegate = this;
this._pendingRedisplay = false;
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedNotify));
this._grid = new WellGrid();
this.actor.append(this._grid.actor, Big.BoxPackFlags.EXPAND);
@ -833,7 +841,20 @@ AppWell.prototype = {
values[id] = index; return values; }, {});
},
_onMappedNotify: function() {
let mapped = this.actor.mapped;
if (mapped && this._pendingRedisplay)
this._redisplay();
},
_redisplay: function () {
let mapped = this.actor.mapped;
if (!mapped) {
this._pendingRedisplay = true;
return;
}
this._pendingRedisplay = false;
this._grid.removeAll();
let favoriteIds = this._appSystem.get_favorites();
@ -849,7 +870,6 @@ AppWell.prototype = {
let displays = []
this._addApps(favorites, true);
this._grid.setSeparatorIndex(favorites.length);
this._addApps(running, false);
this._displays = displays;
},
@ -872,9 +892,7 @@ AppWell.prototype = {
let appSystem = Shell.AppSystem.get_default();
let app = null;
if (source instanceof BaseWellItem) {
app = source.appInfo;
} else if (source instanceof AppDisplayItem) {
if (source instanceof AppDisplayItem) {
app = appSystem.lookup_cached_app(source.getId());
} else if (source instanceof Workspaces.WindowClone) {
let appMonitor = Shell.AppMonitor.get_default();
@ -891,22 +909,15 @@ AppWell.prototype = {
let favoriteIds = this._appSystem.get_favorites();
let favoriteIdsObject = this._arrayValues(favoriteIds);
let dropIsFavorite = this._grid.isBeforeSeparator(x - this._grid.actor.x,
y - this._grid.actor.y);
let srcIsFavorite = (id in favoriteIdsObject);
if (srcIsFavorite && (!dropIsFavorite)) {
Mainloop.idle_add(function () {
appSystem.remove_favorite(id);
return false;
});
} else if ((!srcIsFavorite) && dropIsFavorite) {
if (srcIsFavorite) {
return false;
} else {
Mainloop.idle_add(function () {
appSystem.add_favorite(id);
return false;
});
} else {
return false;
}
return true;

View File

@ -20,7 +20,7 @@ GLOW_COLOR.from_pixel(0x4f6ba4ff);
const GLOW_PADDING_HORIZONTAL = 3;
const GLOW_PADDING_VERTICAL = 3;
const APPICON_ICON_SIZE = 48;
const APPICON_DEFAULT_ICON_SIZE = 48;
const APPICON_PADDING = 1;
const APPICON_BORDER_WIDTH = 1;
@ -49,14 +49,19 @@ TRANSPARENT_COLOR.from_pixel(0x00000000);
const MenuType = { NONE: 0, ON_RIGHT: 1, BELOW: 2 };
function AppIcon(appInfo, menuType) {
this._init(appInfo, menuType || MenuType.NONE);
function AppIcon(params) {
this._init(params);
}
AppIcon.prototype = {
_init : function(appInfo, menuType) {
this.appInfo = appInfo;
this._menuType = menuType;
_init : function(params) {
this.appInfo = params.appInfo;
if (!this.appInfo)
throw new Error('AppIcon constructor requires "appInfo" param');
this._menuType = ('menuType' in params) ? params.menuType : MenuType.NONE;
this._iconSize = ('size' in params) ? params.size : APPICON_DEFAULT_ICON_SIZE;
let showGlow = ('glow' in params) ? params.glow : false;
this.actor = new Shell.ButtonBox({ orientation: Big.BoxOrientation.VERTICAL,
border: APPICON_BORDER_WIDTH,
@ -65,29 +70,36 @@ AppIcon.prototype = {
reactive: true });
this.actor._delegate = this;
this.highlight_border_color = APPICON_DEFAULT_BORDER_COLOR;
this._signalIds = [];
if (menuType != MenuType.NONE) {
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(appInfo.get_id());
for (let i = 0; i < this.windows.length; i++) {
this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
}
this._resortWindows();
// Note, we don't presently update the window list dynamically here; this actor
// gets destroyed and recreated by AppWell, and in alt-tab the whole thing is
// created each time
this.windows = Shell.AppMonitor.get_default().get_windows_for_app(this.appInfo.get_id());
for (let i = 0; i < this.windows.length; i++) {
let sigId = this.windows[i].connect('notify::user-time', Lang.bind(this, this._resortWindows));
this._signalIds.push([this.windows[i], sigId]);
}
this._resortWindows();
this.actor.connect('destroy', Lang.bind(this, this._destroy));
this.actor.connect('notify::mapped', Lang.bind(this, this._onMappedChanged));
if (this._menuType != MenuType.NONE) {
this.actor.connect('button-press-event', Lang.bind(this, this._updateMenuOnButtonPress));
this.actor.connect('notify::hover', Lang.bind(this, this._updateMenuOnHoverChanged));
this.actor.connect('activate', Lang.bind(this, this._updateMenuOnActivate));
this._menuTimeoutId = 0;
this._menu = null;
} else
this.windows = [];
}
let iconBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER,
width: APPICON_ICON_SIZE,
height: APPICON_ICON_SIZE });
this.icon = appInfo.create_icon_texture(APPICON_ICON_SIZE);
width: this._iconSize,
height: this._iconSize });
this.icon = this.appInfo.create_icon_texture(this._iconSize);
iconBox.append(this.icon, Big.BoxPackFlags.NONE);
this.actor.append(iconBox, Big.BoxPackFlags.EXPAND);
@ -102,18 +114,23 @@ AppIcon.prototype = {
font_name: "Sans 12px",
line_alignment: Pango.Alignment.CENTER,
ellipsize: Pango.EllipsizeMode.END,
text: appInfo.get_name() });
text: this.appInfo.get_name() });
nameBox.add_actor(this._name);
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
for (let i = 0; i < this.windows.length && i < 3; i++) {
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
glowPath, -1, -1);
glow.keep_aspect_ratio = false;
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
if (showGlow) {
this._glowBox = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
let glowPath = GLib.filename_to_uri(global.imagedir + 'app-well-glow.png', '');
for (let i = 0; i < this.windows.length && i < 3; i++) {
let glow = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
glowPath, -1, -1);
glow.keep_aspect_ratio = false;
this._glowBox.append(glow, Big.BoxPackFlags.EXPAND);
}
this._nameBox.add_actor(this._glowBox);
this._glowBox.lower(this._name);
}
this._nameBox.add_actor(this._glowBox);
this._glowBox.lower(this._name);
else
this._glowBox = null;
this.actor.append(nameBox, Big.BoxPackFlags.NONE);
},
@ -158,8 +175,36 @@ AppIcon.prototype = {
}
},
_destroy: function() {
for (let i = 0; i < this._signalIds.length; i++) {
let [obj, sigId] = this._signalIds[i];
obj.disconnect(sigId);
}
},
_onMappedChanged: function() {
let mapped = this.actor.mapped;
if (mapped && this._windowSortStale)
this._resortWindows();
},
_resortWindows: function() {
let mapped = this.actor.mapped;
if (!mapped) {
this._windowSortStale = true;
return;
}
this._windowSortStale = false;
this.windows.sort(function (a, b) {
let activeWorkspace = global.screen.get_active_workspace();
let wsA = a.get_workspace() == activeWorkspace;
let wsB = b.get_workspace() == activeWorkspace;
if (wsA && !wsB)
return -1;
else if (wsB && !wsA)
return 1;
let visA = a.showing_on_its_workspace();
let visB = b.showing_on_its_workspace();
@ -176,7 +221,7 @@ AppIcon.prototype = {
// a subclass of it draggable, you can use this method to create
// a drag actor
createDragActor: function() {
return this.appInfo.create_icon_texture(APPICON_ICON_SIZE);
return this.appInfo.create_icon_texture(this._iconSize);
},
setHighlight: function(highlight) {
@ -205,14 +250,19 @@ AppIcon.prototype = {
},
_updateMenuOnButtonPress: function(actor, event) {
if (this._menuTimeoutId != 0)
Mainloop.source_remove(this._menuTimeoutId);
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
Lang.bind(this, this.popupMenu));
let button = event.get_button();
if (button == 1) {
if (this._menuTimeoutId != 0)
Mainloop.source_remove(this._menuTimeoutId);
this._menuTimeoutId = Mainloop.timeout_add(APPICON_MENU_POPUP_TIMEOUT_MS,
Lang.bind(this, function () { this.popupMenu(button); }));
} else if (button == 3) {
this.popupMenu(button);
}
return false;
},
popupMenu: function() {
popupMenu: function(activatingButton) {
if (this._menuTimeoutId != 0) {
Mainloop.source_remove(this._menuTimeoutId);
this._menuTimeoutId = 0;
@ -236,7 +286,7 @@ AppIcon.prototype = {
}));
}
this._menu.popup();
this._menu.popup(activatingButton);
return false;
},
@ -299,7 +349,7 @@ AppIconMenu.prototype = {
this._arrow = new Shell.DrawingArea();
this._arrow.connect('redraw', Lang.bind(this, function (area, texture) {
Shell.draw_box_pointer(texture,
this._type == MenuType.ON_RIGHT ? Clutter.Gravity.WEST : Clutter.Gravity.NORTH,
this._type == MenuType.ON_RIGHT ? Shell.PointerDirection.LEFT : Shell.PointerDirection.UP,
source.highlight_border_color,
APPICON_MENU_BACKGROUND_COLOR);
}));
@ -377,32 +427,53 @@ AppIconMenu.prototype = {
let iconsDiffer = false;
let texCache = Shell.TextureCache.get_default();
let firstIcon = windows[0].mini_icon;
for (let i = 1; i < windows.length; i++) {
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
iconsDiffer = true;
break;
if (windows.length > 0) {
let firstIcon = windows[0].mini_icon;
for (let i = 1; i < windows.length; i++) {
if (!texCache.pixbuf_equal(windows[i].mini_icon, firstIcon)) {
iconsDiffer = true;
break;
}
}
}
// Display the app windows menu items and the separator between windows
// of the current desktop and other windows.
let activeWorkspace = global.screen.get_active_workspace();
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
let currentWorkspaceWindows = windows.filter(function (w) {
return w.get_workspace() == activeWorkspace;
});
let otherWorkspaceWindows = windows.filter(function (w) {
return w.get_workspace() != activeWorkspace;
});
for (let i = 0; i < windows.length; i++) {
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
this._appendSeparator();
separatorShown = true;
}
this._appendWindows(currentWorkspaceWindows, iconsDiffer);
if (currentWorkspaceWindows.length > 0 && otherWorkspaceWindows.length > 0) {
this._appendSeparator();
let icon = null;
if (iconsDiffer)
icon = Shell.TextureCache.get_default().bind_pixbuf_property(windows[i], "mini-icon");
let box = this._appendMenuItem(icon, windows[i].title);
box._window = windows[i];
}
this._appendWindows(otherWorkspaceWindows, iconsDiffer);
this._appendSeparator();
if (windows.length > 0)
this._appendSeparator();
this._newWindowMenuItem = this._appendMenuItem(null, _("New Window"));
this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(null, _("New Window")) : null;
let favorites = Shell.AppSystem.get_default().get_favorites();
let id = this._source.appInfo.get_id();
this._isFavorite = false;
for (let i = 0; i < favorites.length; i++) {
if (id == favorites[i]) {
this._isFavorite = true;
break;
}
}
if (windows.length > 0)
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(null, this._isFavorite ? _("Remove from favorites")
: _("Add to favorites"));
this._highlightedItem = null;
},
@ -441,26 +512,13 @@ AppIconMenu.prototype = {
return box;
},
_appendWindows: function (windows, iconsDiffer) {
for (let i = 0; i < windows.length; i++) {
let metaWindow = windows[i];
let icon = null;
if (iconsDiffer) {
icon = Shell.TextureCache.get_default().bind_pixbuf_property(metaWindow, "mini-icon");
}
let box = this._appendMenuItem(icon, metaWindow.title);
box._window = metaWindow;
}
},
popup: function() {
popup: function(activatingButton) {
let [stageX, stageY] = this._source.actor.get_transformed_position();
let [stageWidth, stageHeight] = this._source.actor.get_transformed_size();
this._redisplay();
this._windowContainer.popup(0, Main.currentTime());
this._windowContainer.popup(activatingButton, Main.currentTime());
this.emit('popup', true);
@ -559,6 +617,12 @@ AppIconMenu.prototype = {
} else if (child == this._newWindowMenuItem) {
this._source.appInfo.launch();
this.emit('activate-window', null);
} else if (child == this._toggleFavoriteMenuItem) {
let appSys = Shell.AppSystem.get_default();
if (this._isFavorite)
appSys.remove_favorite(this._source.appInfo.get_id());
else
appSys.add_favorite(this._source.appInfo.get_id());
}
this.popdown();
},

177
js/ui/calendar.js Normal file
View File

@ -0,0 +1,177 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Gettext_gtk20 = imports.gettext.domain('gtk20');
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
function _sameDay(dateA, dateB) {
return (dateA.getDate() == dateB.getDate() &&
dateA.getMonth() == dateB.getMonth() &&
dateA.getYear() == dateB.getYear());
}
function Calendar() {
this._init();
};
Calendar.prototype = {
_init: function() {
// FIXME: This is actually the fallback method for GTK+ for the week start;
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
// should add a C function so we can do the full handling.
this._weekStart = NaN;
let weekStartString = Gettext_gtk20.gettext("calendar:week_start:0");
if (weekStartString.indexOf("calendar:week_start:") == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
log("Translation of 'calendar:week_start:0' in GTK+ is not correct");
this.weekStart = 0;
}
// Find the ordering for month/year in the calendar heading
switch (Gettext_gtk20.gettext("calendar:MY")) {
case "calendar:MY":
this._headerFormat = "%B %Y";
break;
case "calendar:YM":
this._headerFormat = "%Y %B";
break;
default:
log("Translation of 'calendar:MY' in GTK+ is not correct");
this._headerFormat = "%B %Y";
break;
}
// Start off with the current date
this.date = new Date();
this.actor = new St.Table({ homogeneous: false,
style_class: "calendar",
reactive: true });
this.actor.connect('scroll-event',
Lang.bind(this, this._onScroll));
// Top line of the calendar '<| September 2009 |>'
this._topBox = new St.BoxLayout();
this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: 7 });
let back = new St.Button({ label: "&lt;", style_class: 'calendar-change-month' });
this._topBox.add(back);
back.connect("clicked", Lang.bind(this, this._prevMonth));
this._dateLabel = new St.Label();
this._topBox.add(this._dateLabel, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
let forward = new St.Button({ label: "&gt;", style_class: 'calendar-change-month' });
this._topBox.add(forward);
forward.connect("clicked", Lang.bind(this, this._nextMonth));
// We need to figure out the abbreviated localized names for the days of the week;
// we do this by just getting the next 7 days starting from right now and then putting
// them in the right cell in the table. It doesn't matter if we add them in order
let iter = new Date(this.date);
iter.setSeconds(0); // Leap second protection. Hah!
iter.setHours(12);
for (let i = 0; i < 7; i++) {
this.actor.add(new St.Label({ text: iter.toLocaleFormat("%a") }),
{ row: 1,
col: (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: 1.0 });
iter.setTime(iter.getTime() + MSECS_IN_DAY);
}
// All the children after this are days, and get removed when we update the calendar
this._firstDayIndex = this.actor.get_children().length;
this._update();
},
// Sets the calendar to show a specific date
setDate: function(date) {
if (!_sameDay(date, this.date)) {
this.date = date;
this._update();
}
},
_onScroll : function(actor, event) {
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
case Clutter.ScrollDirection.LEFT:
this._prevMonth();
break;
case Clutter.ScrollDirection.DOWN:
case Clutter.ScrollDirection.RIGHT:
this._nextMonth();
break;
}
},
_prevMonth: function() {
if (this.date.getMonth() == 0) {
this.date.setMonth(11);
this.date.setFullYear(this.date.getFullYear() - 1);
} else {
this.date.setMonth(this.date.getMonth() - 1);
}
this._update();
},
_nextMonth: function() {
if (this.date.getMonth() == 11) {
this.date.setMonth(0);
this.date.setFullYear(this.date.getFullYear() + 1);
} else {
this.date.setMonth(this.date.getMonth() + 1);
}
this._update();
},
_update: function() {
this._dateLabel.text = this.date.toLocaleFormat(this._headerFormat);
// Remove everything but the topBox and the weekday labels
let children = this.actor.get_children();
for (let i = this._firstDayIndex; i < children.length; i++)
children[i].destroy();
// Start at the beginning of the week before the start of the month
let iter = new Date(this.date);
iter.setDate(1);
iter.setSeconds(0);
iter.setHours(12);
iter.setTime(iter.getTime() - (iter.getDay() - this._weekStart) * MSECS_IN_DAY);
let now = new Date();
let row = 2;
while (true) {
let label = new St.Label({ text: iter.getDate().toString() });
if (_sameDay(now, iter))
label.style_class = "calendar-day calendar-today";
else if (iter.getMonth() != this.date.getMonth())
label.style_class = "calendar-day calendar-other-month-day";
else
label.style_class = "calendar-day";
this.actor.add(label,
{ row: row, col: (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: 1.0 });
iter.setTime(iter.getTime() + MSECS_IN_DAY);
if (iter.getDay() == this._weekStart) {
// We stop on the first "first day of the week" after the month we are displaying
if (iter.getMonth() > this.date.getMonth() || iter.getYear() > this.date.getYear())
break;
row++;
}
}
}
};

View File

@ -889,6 +889,9 @@ Dash.prototype = {
_updateDashActors: function() {
if (this._searchPending) {
this._searchResultsSection.actor.show();
// We initially hide all sections when we start a search. When the search timeout
// first runs, the sections that have matching results are shown. As the search
// is refined, only the sections that have matching results will be shown.
for (let i = 0; i < this._searchSections.length; i++) {
let section = this._searchSections[i];
section.header.actor.hide();
@ -897,13 +900,7 @@ Dash.prototype = {
this._appsSection.actor.hide();
this._placesSection.actor.hide();
this._docsSection.actor.hide();
} else if (this._searchActive) {
for (let i = 0; i < this._searchSections.length; i++) {
let section = this._searchSections[i];
section.header.actor.show();
section.resultArea.actor.show();
}
} else {
} else if (!this._searchActive) {
this._showAllSearchSections();
this._searchResultsSection.actor.hide();
this._appsSection.actor.show();

View File

@ -136,7 +136,7 @@ DocDisplay.prototype = {
// but redisplaying right away is cool when we use Zephyr.
// Also, we might be displaying remote documents, like Google Docs, in the future
// which might be edited by someone else.
this._redisplay(false);
this._redisplay(GenericDisplay.RedisplayFlags.NONE);
}));
this.connect('destroy', Lang.bind(this, function (o) {

36
js/ui/environment.js Normal file
View File

@ -0,0 +1,36 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const St = imports.gi.St;
const Tweener = imports.ui.tweener;
const Format = imports.misc.format;
// "monkey patch" in some varargs ClutterContainer methods; we need
// to do this per-container class since there is no representation
// of interfaces in Javascript
function _patchContainerClass(containerClass) {
// This one is a straightforward mapping of the C method
containerClass.prototype.child_set = function(actor, props) {
let meta = this.get_child_meta(actor);
for (prop in props)
meta[prop] = props[prop];
};
// clutter_container_add() actually is a an add-many-actors
// method. We conveniently, but somewhat dubiously, take the
// this opportunity to make it do something more useful.
containerClass.prototype.add = function(actor, props) {
this.add_actor(actor);
if (props)
this.child_set(actor, props);
};
}
_patchContainerClass(St.BoxLayout);
_patchContainerClass(St.Table);
function init() {
Tweener.init();
String.prototype.format = Format.format;
}

View File

@ -370,7 +370,11 @@ GenericDisplay.prototype = {
return;
let flags = RedisplayFlags.RESET_CONTROLS;
if (this._search != '') {
if (lowertext.indexOf(this._search) == 0)
// Because we combine search terms with OR, we have to be sure that no new term
// was introduced before deciding that the new search results will be a subset of
// the existing search results.
if (lowertext.indexOf(this._search) == 0 &&
lowertext.split(/\s+/).length == this._search.split(/\s+/).length)
flags |= RedisplayFlags.SUBSEARCH;
}
this._search = lowertext;
@ -486,15 +490,16 @@ GenericDisplay.prototype = {
//// Protected methods ////
_redisplayFull: function() {
_recreateDisplayItems: function() {
this._removeAllDisplayItems();
this._setDefaultList();
for (let itemId in this._allItems) {
this._addDisplayItem(itemId);
}
},
// Creates a display item based on the information associated with itemId
// and adds it to the displayed items.
// and adds it to the list of displayed items, but does not yet display it.
_addDisplayItem : function(itemId) {
if (this._displayedItems.hasOwnProperty(itemId)) {
log("Tried adding a display item for " + itemId + ", but an item with this item id is already among displayed items.");
@ -525,7 +530,6 @@ GenericDisplay.prototype = {
this.emit('show-details', index);
}
}));
this._list.add_actor(displayItem.actor);
this._displayedItems[itemId] = displayItem;
},
@ -635,6 +639,7 @@ GenericDisplay.prototype = {
* their own while the user was browsing through the result pages.
* SUBSEARCH - Indicates that the current _search is a superstring of the previous
* one, which implies we only need to re-search through previous results.
* FULL - Indicates that we need refresh all displayed items.
*/
_redisplay: function(flags) {
let resetPage = (flags & RedisplayFlags.RESET_CONTROLS) > 0;
@ -642,13 +647,20 @@ GenericDisplay.prototype = {
let fullReload = (flags & RedisplayFlags.FULL) > 0;
let hadSelected = this.hasSelected();
this.unsetSelected();
if (!this._initialLoadComplete || !this._refreshCache())
if (!this._initialLoadComplete)
fullReload = true;
if (!this._refreshCache())
fullReload = true;
if (fullReload) {
this._recreateDisplayItems();
this._initialLoadComplete = true;
this._redisplayFull();
} if (isSubSearch) {
}
if (isSubSearch) {
this._redisplaySubSearch();
} else {
this._redisplayReordering();

View File

@ -4,6 +4,7 @@ const Big = imports.gi.Big;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Lang = imports.lang;
@ -12,18 +13,7 @@ const Mainloop = imports.mainloop;
const Tweener = imports.ui.tweener;
const Main = imports.ui.main;
const LG_BORDER_COLOR = new Clutter.Color();
LG_BORDER_COLOR.from_pixel(0x0000aca0);
const LG_BACKGROUND_COLOR = new Clutter.Color();
LG_BACKGROUND_COLOR.from_pixel(0x000000d5);
const GREY = new Clutter.Color();
GREY.from_pixel(0xAFAFAFFF);
const MATRIX_GREEN = new Clutter.Color();
MATRIX_GREEN.from_pixel(0x88ff66ff);
// FIXME pull from GConf
const MATRIX_FONT = 'Monospace 10';
/* Imports...feel free to add here as needed */
/* Imports...feel free to add here as needed */
var commandHeader = "const Clutter = imports.gi.Clutter; " +
"const GLib = imports.gi.GLib; " +
"const Gtk = imports.gi.Gtk; " +
@ -47,7 +37,7 @@ function Notebook() {
Notebook.prototype = {
_init: function() {
this.actor = new Big.Box();
this.actor = new St.BoxLayout({ vertical: true });
this.tabControls = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4, padding: 2 });
@ -58,21 +48,24 @@ Notebook.prototype = {
appendPage: function(name, child) {
let labelOuterBox = new Big.Box({ padding: 2 });
let labelBox = new Big.Box({ padding: 2, border_color: MATRIX_GREEN,
reactive: true });
let labelBox = new St.BoxLayout({ reactive: true });
labelOuterBox.append(labelBox, Big.BoxPackFlags.NONE);
let label = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
text: name });
let label = new St.Label({ text: name });
labelBox.connect('button-press-event', Lang.bind(this, function () {
this.selectChild(child);
return true;
}));
labelBox.append(label, Big.BoxPackFlags.EXPAND);
this._tabs.push([child, labelBox]);
child.hide();
this.actor.append(child, Big.BoxPackFlags.EXPAND);
labelBox.add(label, { expand: true });
this.tabControls.append(labelOuterBox, Big.BoxPackFlags.NONE);
let scrollview = new St.ScrollView({ x_fill: true, y_fill: true });
scrollview.get_hscroll_bar().hide();
scrollview.add_actor(child);
this._tabs.push([child, labelBox, scrollview]);
scrollview.hide();
this.actor.add(scrollview, { expand: true });
if (this._selectedIndex == -1)
this.selectIndex(0);
},
@ -80,10 +73,10 @@ Notebook.prototype = {
_unselect: function() {
if (this._selectedIndex < 0)
return;
let [child, labelBox] = this._tabs[this._selectedIndex];
let [child, labelBox, scrollview] = this._tabs[this._selectedIndex];
labelBox.padding = 2;
labelBox.border = 0;
child.hide();
scrollview.hide();
this._selectedIndex = -1;
},
@ -95,10 +88,10 @@ Notebook.prototype = {
this.emit('selection', null);
return;
}
let [child, labelBox] = this._tabs[index];
let [child, labelBox, scrollview] = this._tabs[index];
labelBox.padding = 1;
labelBox.border = 1;
child.show();
scrollview.show();
this._selectedIndex = index;
this.emit('selection', child);
},
@ -108,7 +101,7 @@ Notebook.prototype = {
this.selectIndex(-1);
else {
for (let i = 0; i < this._tabs.length; i++) {
let [tabChild, labelBox] = this._tabs[i];
let [tabChild, labelBox, scrollview] = this._tabs[i];
if (tabChild == child) {
this.selectIndex(i);
return;
@ -130,20 +123,19 @@ Result.prototype = {
this.actor = new Big.Box();
let cmdTxt = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
ellipsize: Pango.EllipsizeMode.END,
text: command });
let cmdTxt = new St.Label({ text: command });
cmdTxt.ellipsize = Pango.EllipsizeMode.END;
this.actor.append(cmdTxt, Big.BoxPackFlags.NONE);
let resultTxt = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
ellipsize: Pango.EllipsizeMode.END,
text: "r(" + index + ") = " + o });
let resultTxt = new St.Label({ text: "r(" + index + ") = " + o });
resultTxt.ellipsize = Pango.EllipsizeMode.END;
this.actor.append(resultTxt, Big.BoxPackFlags.NONE);
let line = new Big.Box({ border_color: GREY,
border_bottom: 1,
height: 8 });
this.actor.append(line, Big.BoxPackFlags.NONE);
let line = new Clutter.Rectangle({ name: "Separator",
height: 1 });
let padBin = new St.Bin({ name: "Separator", x_fill: true, y_fill: true });
padBin.add_actor(line);
this.actor.append(padBin, Big.BoxPackFlags.NONE);
}
}
@ -158,17 +150,14 @@ ActorHierarchy.prototype = {
this._parentList = [];
this.actor = new Big.Box({ spacing: 4,
border: 1,
padding: 4,
border_color: GREY });
this.actor = new St.BoxLayout({ name: "ActorHierarchy", vertical: true });
},
setTarget: function(actor) {
this._previousTarget = this._target;
this.target = actor;
this.actor.remove_all();
this.actor.get_children().forEach(function (child) { child.destroy(); });
if (!(actor instanceof Clutter.Actor))
return;
@ -181,11 +170,9 @@ ActorHierarchy.prototype = {
while ((parent = parent.get_parent()) != null) {
this._parentList.push(parent);
let link = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
reactive: true,
text: "" + parent });
this.actor.append(link, Big.BoxPackFlags.IF_FITS);
let link = new St.Label({ reactive: true,
text: "" + parent });
this.actor.add_actor(link);
let parentTarget = parent;
link.connect('button-press-event', Lang.bind(this, function () {
this._selectByActor(parentTarget);
@ -214,16 +201,13 @@ PropertyInspector.prototype = {
this._parentList = [];
this.actor = new Big.Box({ spacing: 4,
border: 1,
padding: 4,
border_color: GREY });
this.actor = new St.BoxLayout({ name: "PropertyInspector", vertical: true });
},
setTarget: function(actor) {
this.target = actor;
this.actor.remove_all();
this.actor.get_children().forEach(function (child) { child.destroy(); });
for (let propName in actor) {
let valueStr;
@ -233,11 +217,9 @@ PropertyInspector.prototype = {
valueStr = '<error>';
}
let propText = propName + ": " + valueStr;
let propDisplay = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
reactive: true,
text: propText });
this.actor.append(propDisplay, Big.BoxPackFlags.IF_FITS);
let propDisplay = new St.Label({ reactive: true,
text: propText });
this.actor.add_actor(propDisplay);
}
}
}
@ -249,20 +231,17 @@ function Inspector() {
Inspector.prototype = {
_init: function() {
let width = 150;
let eventHandler = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
border: 1,
border_color: LG_BORDER_COLOR,
corner_radius: 4,
y: global.stage.height/2,
reactive: true
});
let primary = global.get_primary_monitor();
let eventHandler = new St.BoxLayout({ name: "LookingGlassDialog",
vertical: false,
y: primary.y + Math.floor(primary.height / 2),
reactive: true });
eventHandler.connect('notify::allocation', Lang.bind(this, function () {
eventHandler.x = Math.floor((global.stage.width)/2 - (eventHandler.width)/2);
eventHandler.x = primary.x + Math.floor((primary.width - eventHandler.width) / 2);
}));
global.stage.add_actor(eventHandler);
let displayText = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT, text: '' });
eventHandler.append(displayText, Big.BoxPackFlags.EXPAND);
let displayText = new St.Label();
eventHandler.add(displayText, { expand: true });
let borderPaintTarget = null;
let borderPaintId = null;
@ -321,29 +300,26 @@ LookingGlass.prototype = {
this._offset = 0;
this._results = [];
// TODO replace with scrolling or something better
this._maxItems = 10;
// Sort of magic, but...eh.
this._maxItems = 150;
this.actor = new St.BoxLayout({ name: "LookingGlassDialog",
vertical: true,
visible: false });
let gconf = Shell.GConf.get_default();
gconf.watch_directory("/desktop/gnome/interface");
gconf.connect("changed::/desktop/gnome/interface/monospace_font_name",
Lang.bind(this, this._updateFont));
this._updateFont();
this.actor = new Big.Box({ background_color: LG_BACKGROUND_COLOR,
border: 1,
border_color: LG_BORDER_COLOR,
corner_radius: 4,
padding_top: 8,
padding_left: 4,
padding_right: 4,
padding_bottom: 4,
spacing: 4,
visible: false
});
global.stage.add_actor(this.actor);
let toolbar = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
border: 1, border_color: GREY,
corner_radius: 4 });
this.actor.append(toolbar, Big.BoxPackFlags.NONE);
let toolbar = new St.BoxLayout({ name: "Toolbar" });
this.actor.add_actor(toolbar);
let inspectIcon = Shell.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
24);
toolbar.append(inspectIcon, Big.BoxPackFlags.NONE);
toolbar.add_actor(inspectIcon);
inspectIcon.reactive = true;
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector();
@ -361,31 +337,26 @@ LookingGlass.prototype = {
}));
let notebook = new Notebook();
this.actor.append(notebook.actor, Big.BoxPackFlags.EXPAND);
toolbar.append(notebook.tabControls, Big.BoxPackFlags.END);
this.actor.add(notebook.actor, { expand: true });
this._evalBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: 4 });
let emptyBox = new St.Bin();
toolbar.add(emptyBox, { expand: true });
toolbar.add_actor(notebook.tabControls);
this._evalBox = new St.BoxLayout({ name: "EvalBox", vertical: true });
notebook.appendPage('Evaluator', this._evalBox);
this._resultsArea = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: 4 });
this._evalBox.append(this._resultsArea, Big.BoxPackFlags.EXPAND);
this._evalBox.add(this._resultsArea, { expand: true });
let entryArea = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
this._evalBox.append(entryArea, Big.BoxPackFlags.NONE);
this._evalBox.add_actor(entryArea);
let label = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
text: 'js>>> ' });
let label = new St.Label({ text: 'js>>> ' });
entryArea.append(label, Big.BoxPackFlags.NONE);
this._entry = new Clutter.Text({ color: MATRIX_GREEN,
font_name: MATRIX_FONT,
editable: true,
activatable: true,
singleLineMode: true,
text: ''});
this._entry = new St.Entry();
/* unmapping the edit box will un-focus it, undo that */
notebook.connect('selection', Lang.bind(this, function (nb, child) {
if (child == this._evalBox)
@ -403,7 +374,7 @@ LookingGlass.prototype = {
notebook.selectIndex(0);
}));
this._entry.connect('activate', Lang.bind(this, function (o, e) {
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
let text = o.get_text();
// Ensure we don't get newlines in the command; the history file is
// newline-separated.
@ -416,7 +387,7 @@ LookingGlass.prototype = {
this._historyNavIndex = -1;
return true;
}));
this._entry.connect('key-press-event', Lang.bind(this, function(o, e) {
this._entry.clutter_text.connect('key-press-event', Lang.bind(this, function(o, e) {
let symbol = e.get_key_symbol();
if (symbol == Clutter.Escape) {
this.close();
@ -446,6 +417,19 @@ LookingGlass.prototype = {
}));
},
_updateFont: function() {
let gconf = Shell.GConf.get_default();
let fontName = gconf.get_string("/desktop/gnome/interface/monospace_font_name");
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
let fontDesc = Pango.Font.description_from_string(fontName);
// We ignore everything but size and style; you'd be crazy to set your system-wide
// monospace font to be bold/oblique/etc. Could easily be added here.
this.actor.style =
'font-size: ' + fontDesc.get_size() / 1024. + (fontDesc.get_size_is_absolute() ? 'px' : 'pt') + ';'
+ 'font-family: "' + fontDesc.get_family() + '";';
},
_readHistory: function () {
if (!this._historyFile.query_exists(null))
return;
@ -518,11 +502,11 @@ LookingGlass.prototype = {
},
_resizeTo: function(actor) {
let stage = global.stage;
let myWidth = stage.width * 0.7;
let myHeight = stage.height * 0.7;
let primary = global.get_primary_monitor();
let myWidth = primary.width * 0.7;
let myHeight = primary.height * 0.7;
let [srcX, srcY] = actor.get_transformed_position();
this.actor.x = srcX + (stage.width-myWidth)/2;
this.actor.x = srcX + (primary.width - myWidth) / 2;
this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY;

View File

@ -10,15 +10,16 @@ const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Chrome = imports.ui.chrome;
const Environment = imports.ui.environment;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const RunDialog = imports.ui.runDialog;
const LookingGlass = imports.ui.lookingGlass;
const ShellDBus = imports.ui.shellDBus;
const Sidebar = imports.ui.sidebar;
const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
@ -52,7 +53,7 @@ function start() {
// back into sync ones.
DBus.session.flush();
Tweener.init();
Environment.init();
// Ensure ShellAppMonitor is initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
@ -75,6 +76,11 @@ function start() {
for (let i = 0; i < children.length; i++)
children[i].destroy();
let themeContext = St.ThemeContext.get_for_stage (global.stage);
let stylesheetPath = global.datadir + "/theme/gnome-shell.css";
let theme = new St.Theme ({ application_stylesheet: stylesheetPath });
themeContext.set_theme (theme);
global.connect('panel-run-dialog', function(panel) {
// Make sure not more than one run dialog is shown.
getRunDialog().open();
@ -121,7 +127,9 @@ function start() {
}
function _relayout() {
panel.actor.set_size(global.screen_width, Panel.PANEL_HEIGHT);
let primary = global.get_primary_monitor();
panel.actor.set_position(primary.x, primary.y);
panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
overview.relayout();
}
@ -193,7 +201,7 @@ function _globalKeyPressHandler(actor, event) {
overview.hide();
return true;
} else if (symbol == Clutter.F2 && (event.get_state() & Clutter.ModifierType.MOD1_MASK)) {
} else if (symbol == Clutter.F2 && (Shell.get_event_state(event) & Clutter.ModifierType.MOD1_MASK)) {
getRunDialog().open();
}
}

View File

@ -142,42 +142,44 @@ Overview.prototype = {
},
_recalculateGridSizes: function () {
wideScreen = (global.screen_width/global.screen_height > WIDE_SCREEN_CUT_OFF_RATIO) &&
(global.screen_height >= WIDE_SCREEN_MINIMUM_HEIGHT);
let primary = global.get_primary_monitor();
wideScreen = (primary.width/primary.height > WIDE_SCREEN_CUT_OFF_RATIO) &&
(primary.height >= WIDE_SCREEN_MINIMUM_HEIGHT);
// We divide the screen into an imaginary grid which helps us determine the layout of
// different visual components.
if (wideScreen) {
displayGridColumnWidth = global.screen_width / COLUMNS_WIDE_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_WIDE_SCREEN;
displayGridColumnWidth = primary.width / COLUMNS_WIDE_SCREEN;
displayGridRowHeight = primary.height / ROWS_WIDE_SCREEN;
} else {
displayGridColumnWidth = global.screen_width / COLUMNS_REGULAR_SCREEN;
displayGridRowHeight = global.screen_height / ROWS_REGULAR_SCREEN;
displayGridColumnWidth = primary.width / COLUMNS_REGULAR_SCREEN;
displayGridRowHeight = primary.height / ROWS_REGULAR_SCREEN;
}
},
relayout: function () {
let screenHeight = global.screen_height;
let screenWidth = global.screen_width;
let primary = global.get_primary_monitor();
this._group.set_position(primary.x, primary.y);
let contentY = Panel.PANEL_HEIGHT;
let contentHeight = screenHeight - contentY;
let contentHeight = primary.height - contentY;
this._coverPane.set_position(0, contentY);
this._coverPane.set_size(screenWidth, contentHeight);
this._coverPane.set_size(primary.width, contentHeight);
let workspaceColumnsUsed = wideScreen ? COLUMNS_FOR_WORKSPACES_WIDE_SCREEN : COLUMNS_FOR_WORKSPACES_REGULAR_SCREEN;
let workspaceRowsUsed = wideScreen ? ROWS_FOR_WORKSPACES_WIDE_SCREEN : ROWS_FOR_WORKSPACES_REGULAR_SCREEN;
this._workspacesWidth = displayGridColumnWidth * workspaceColumnsUsed
- WORKSPACE_GRID_PADDING * 2;
// We scale the vertical padding by (screenHeight / screenWidth)
// We scale the vertical padding by (primary.height / primary.width)
// so that the workspace preserves its aspect ratio.
this._workspacesHeight = displayGridRowHeight * workspaceRowsUsed
- WORKSPACE_GRID_PADDING * (screenHeight / screenWidth) * 2;
- WORKSPACE_GRID_PADDING * (primary.height / primary.width) * 2;
this._workspacesX = displayGridColumnWidth + WORKSPACE_GRID_PADDING;
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (screenHeight / screenWidth);
this._workspacesY = displayGridRowHeight + WORKSPACE_GRID_PADDING * (primary.height / primary.width);
this._dash.actor.set_position(0, contentY);
this._dash.actor.set_size(displayGridColumnWidth, contentHeight);
@ -187,10 +189,12 @@ Overview.prototype = {
// place the 'Add Workspace' button in the bottom row of the grid
addRemoveButtonSize = Math.floor(displayGridRowHeight * 3/5);
this._addButtonX = this._workspacesX + this._workspacesWidth - addRemoveButtonSize;
this._addButtonY = screenHeight - Math.floor(displayGridRowHeight * 4/5);
this._addButtonY = primary.height - Math.floor(displayGridRowHeight * 4/5);
this._backOver.set_position(0, contentY);
this._backOver.set_size(global.screen_width, contentHeight);
// The parent (this._group) is positioned at the top left of the primary monitor
// while this._backOver occupies the entire screen.
this._backOver.set_position(- primary.x, - primary.y);
this._backOver.set_size(global.screen_width, global.screen_height);
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
contentY);
@ -198,7 +202,7 @@ Overview.prototype = {
this._paneContainer.height = contentHeight;
this._transparentBackground.set_position(this._paneContainer.x, this._paneContainer.y);
this._transparentBackground.set_size(global.screen_width - this._paneContainer.x,
this._transparentBackground.set_size(primary.width - this._paneContainer.x,
this._paneContainer.height);
if (this._activeDisplayPane != null)
@ -322,9 +326,10 @@ Overview.prototype = {
// The opposite transition is used in hide().
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
[this._group.x, this._group.y] = this.getZoomedInPosition();
let primary = global.get_primary_monitor();
Tweener.addTween(this._group,
{ x: 0,
y: 0,
{ x: primary.x,
y: primary.y,
scaleX: 1,
scaleY: 1,
transition: 'easeOutQuad',
@ -355,6 +360,10 @@ Overview.prototype = {
this._activeDisplayPane.close();
this._workspaces.hide();
this._addButton.actor.destroy();
this._addButton.actor = null;
this._addButton = null;
// Create a zoom in effect by transforming the Overview group so that
// the active workspace fills up the whole screen. The opposite
// transition is used in show().

View File

@ -7,12 +7,14 @@ const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tweener = imports.ui.tweener;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const Button = imports.ui.button;
const Calendar = imports.ui.calendar;
const Main = imports.ui.main;
const PANEL_HEIGHT = 26;
@ -53,12 +55,13 @@ const TRAY_SPACING_MIN = 8;
// Used for the tray icon container with gtk pre-2.16, which doesn't
// fully support tray icon transparency
const TRAY_BACKGROUND_COLOR = new Clutter.Color();
TRAY_BACKGROUND_COLOR.from_pixel(0xefefefff);
TRAY_BACKGROUND_COLOR.from_pixel(0x0b0b0bff);
const TRAY_BORDER_COLOR = new Clutter.Color();
TRAY_BORDER_COLOR.from_pixel(0x00000033);
const TRAY_CORNER_RADIUS = 5;
const TRAY_BORDER_WIDTH = 0;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
function AppPanelMenu() {
this._init();
@ -288,6 +291,8 @@ Panel.prototype = {
opacity: 0,
reactive: true });
this._hotCornerActivationTime = 0;
this._hotCornerEnvirons.connect('leave-event',
Lang.bind(this, this._onHotCornerEnvironsLeft));
// Clicking on the hot corner environs should result in the same bahavior
@ -313,10 +318,17 @@ Panel.prototype = {
/* center */
let clockButton = new St.Button({ style_class: "panel-button",
toggle_mode: true });
this._centerBox.append(clockButton, Big.BoxPackFlags.NONE);
clockButton.connect('clicked', Lang.bind(this, this._toggleCalendar));
this._clock = new Clutter.Text({ font_name: DEFAULT_FONT,
color: PANEL_FOREGROUND_COLOR,
text: "" });
this._centerBox.append(this._clock, Big.BoxPackFlags.NONE);
clockButton.add_actor(this._clock);
this._calendarPopup = null;
/* right */
@ -395,14 +407,14 @@ Panel.prototype = {
// We get into the Overview mode on button-press-event as opposed to button-release-event because eventually we'll probably
// have the Overview act like a menu that allows the user to release the mouse on the activity the user wants
// to switch to.
this.button.actor.connect('button-press-event', function(b, e) {
if (e.get_button() == 1 && e.get_click_count() == 1) {
Main.overview.toggle();
this.button.actor.connect('button-press-event', Lang.bind(this, function(b, e) {
if (e.get_button() == 1 && e.get_click_count() == 1 && !Main.overview.animationInProgress) {
this._maybeToggleOverviewOnClick();
return true;
} else {
return false;
}
});
}));
// In addition to pressing the button, the Overview can be entered and exited by other means, such as
// pressing the System key, Alt+F1 or Esc. We want the button to be pressed in when the Overview is entered
// and to be released when it is exited regardless of how it was triggered.
@ -454,10 +466,21 @@ Panel.prototype = {
return false;
},
_toggleCalendar: function(clockButton) {
if (clockButton.checked) {
if (this._calendarPopup == null)
this._calendarPopup = new CalendarPopup();
this._calendarPopup.show();
} else {
this._calendarPopup.hide();
}
},
_onHotCornerEntered : function() {
if (!this._hotCornerEntered) {
this._hotCornerEntered = true;
if (!Main.overview.animationInProgress) {
this._hotCornerActivationTime = Date.now() / 1000;
Main.overview.toggle();
}
}
@ -466,7 +489,7 @@ Panel.prototype = {
_onHotCornerClicked : function() {
if (!Main.overview.animationInProgress) {
Main.overview.toggle();
this._maybeToggleOverviewOnClick();
}
return false;
},
@ -483,5 +506,65 @@ Panel.prototype = {
this._hotCornerEntered = false;
}
return false;
},
// Toggles the overview unless this is the first click on the Activities button within the HOT_CORNER_ACTIVATION_TIMEOUT time
// of the hot corner being triggered. This check avoids opening and closing the overview if the user both triggered the hot corner
// and clicked the Activities button.
_maybeToggleOverviewOnClick: function() {
if (this._hotCornerActivationTime == 0 || Date.now() / 1000 - this._hotCornerActivationTime > HOT_CORNER_ACTIVATION_TIMEOUT)
Main.overview.toggle();
this._hotCornerActivationTime = 0;
}
};
function CalendarPopup() {
this._init();
}
CalendarPopup.prototype = {
_init: function() {
let panelActor = Main.panel.actor;
this.actor = new St.BoxLayout({ name: 'calendarPopup' });
this.calendar = new Calendar.Calendar();
this.actor.add(this.calendar.actor);
// Directly adding the actor to Main.chrome.actor is a hack to
// work around the fact that there is no way to add an actor that
// affects the input region but not the shape.
// See: https://bugzilla.gnome.org/show_bug.cgi?id=597044
Main.chrome.actor.add_actor(this.actor);
Main.chrome.addInputRegionActor(this.actor);
this.actor.y = (panelActor.y + panelActor.height - this.actor.height);
},
show: function() {
let panelActor = Main.panel.actor;
// Reset the calendar to today's date
this.calendar.setDate(new Date());
this.actor.x = Math.round(panelActor.x + (panelActor.width - this.actor.width) / 2);
this.actor.lower(panelActor);
this.actor.show();
Tweener.addTween(this.actor,
{ y: panelActor.y + panelActor.height,
time: 0.2,
transition: "easeOutQuad"
});
},
hide: function() {
let panelActor = Main.panel.actor;
Tweener.addTween(this.actor,
{ y: panelActor.y + panelActor.height - this.actor.height,
time: 0.2,
transition: "easeOutQuad",
onComplete: function() { this.actor.hide(); },
onCompleteScope: this
});
}
};

View File

@ -9,11 +9,16 @@ const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const GenericDisplay = imports.ui.genericDisplay;
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
const PLACES_VSPACING = 8;
const PLACES_ICON_SIZE = 16;
@ -75,17 +80,33 @@ function Places() {
Places.prototype = {
_init : function() {
// Places is divided semi-arbitrarily into left and right; a grid would
// look better in that there would be an even number of items left+right,
// but it seems like we want some sort of differentiation between actions
// like "Connect to server..." and regular folders
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
spacing: 4 });
this._menuBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PLACES_VSPACING });
this.actor.append(this._menuBox, Big.BoxPackFlags.EXPAND);
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PLACES_VSPACING });
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
this.actor.append(this._leftBox, Big.BoxPackFlags.EXPAND);
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
this.actor.append(this._rightBox, Big.BoxPackFlags.EXPAND);
// Subdivide left into actions and devices
this._actionsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PLACES_VSPACING });
this._leftBox.append(this._actionsBox, Big.BoxPackFlags.NONE);
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PLACES_VSPACING,
padding_top: 6 });
this._leftBox.append(this._devBox, Big.BoxPackFlags.NONE);
// Right is bookmarks
this._dirsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PLACES_VSPACING });
this.actor.append(this._dirsBox, Big.BoxPackFlags.EXPAND);
this._rightBox.append(this._dirsBox, Big.BoxPackFlags.NONE);
let gconf = Shell.GConf.get_default();
gconf.watch_directory(NAUTILUS_PREFS_DIR);
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
let homeUri = homeFile.get_uri();
@ -99,13 +120,27 @@ Places.prototype = {
Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext());
});
this._menuBox.append(home.actor, Big.BoxPackFlags.NONE);
this._actionsBox.append(home.actor, Big.BoxPackFlags.NONE);
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
let desktopFile = Gio.file_new_for_path (desktopPath);
let desktopUri = desktopFile.get_uri();
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
this._desktopMenu = new PlaceDisplay(desktopLabel,
function() {
return Shell.TextureCache.get_default().load_gicon(desktopIcon, PLACES_ICON_SIZE);
},
function() {
Gio.app_info_launch_default_for_uri(desktopUri, Main.createAppLaunchContext());
});
this._actionsBox.append(this._desktopMenu.actor, Big.BoxPackFlags.NONE);
this._updateDesktopMenuVisibility();
gconf.connect('changed::' + DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
/*
* Show devices, code more or less ported from nautilus-places-sidebar.c
*/
this._menuBox.append(this._devBox, Big.BoxPackFlags.NONE);
this._volumeMonitor = Gio.VolumeMonitor.get();
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
@ -137,17 +172,17 @@ Places.prototype = {
function () {
networkApp.launch();
});
this._menuBox.append(network.actor, Big.BoxPackFlags.NONE);
this._actionsBox.append(network.actor, Big.BoxPackFlags.NONE);
}
let connect = new PlaceDisplay('Connect to...',
let connect = new PlaceDisplay(_("Connect to..."),
function () {
return Shell.TextureCache.get_default().load_icon_name("applications-internet", PLACES_ICON_SIZE);
},
function () {
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
});
this._menuBox.append(connect.actor, Big.BoxPackFlags.NONE);
this._actionsBox.append(connect.actor, Big.BoxPackFlags.NONE);
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]);
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
@ -272,7 +307,15 @@ Places.prototype = {
Gio.app_info_launch_default_for_uri(mountUri, Main.createAppLaunchContext());
});
this._devBox.append(devItem.actor, Big.BoxPackFlags.NONE);
}
},
_updateDesktopMenuVisibility: function() {
let gconf = Shell.GConf.get_default();
let desktopIsHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
if (desktopIsHome)
this._desktopMenu.actor.hide();
else
this._desktopMenu.actor.show();
}
};
Signals.addSignalMethods(Places.prototype);

View File

@ -60,24 +60,26 @@ RunDialog.prototype = {
// All actors are inside _group. We create it initially
// hidden then show it in show()
this._group = new Clutter.Group({ visible: false });
this._group = new Clutter.Group({ visible: false,
x: 0,
y: 0,
width: global.screen_width,
height: global.screen_height });
global.stage.add_actor(this._group);
this._lightbox = new Lightbox.Lightbox(this._group);
let lightbox = new Lightbox.Lightbox(this._group);
let boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER,
width: global.screen_width,
height: global.screen_height });
this._boxH = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
x_align: Big.BoxAlignment.CENTER,
y_align: Big.BoxAlignment.CENTER });
this._group.add_actor(boxH);
this._lightbox.highlight(boxH);
this._group.add_actor(this._boxH);
lightbox.highlight(this._boxH);
let boxV = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
y_align: Big.BoxAlignment.CENTER });
boxH.append(boxV, Big.BoxPackFlags.NONE);
this._boxH.append(boxV, Big.BoxPackFlags.NONE);
let dialogBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
@ -87,7 +89,7 @@ RunDialog.prototype = {
padding: DIALOG_PADDING,
width: DIALOG_WIDTH });
boxH.append(dialogBox, Big.BoxPackFlags.NONE);
this._boxH.append(dialogBox, Big.BoxPackFlags.NONE);
let label = new Clutter.Text({ color: BOX_TEXT_COLOR,
font_name: '18px Sans',
@ -146,6 +148,7 @@ RunDialog.prototype = {
},
_run : function(command) {
this._commandError = false;
let f;
if (this._enableInternalCommands)
f = this._internalCommands[command];
@ -155,7 +158,6 @@ RunDialog.prototype = {
f();
} else if (command) {
try {
this._commandError = false;
let [ok, len, args] = GLib.shell_parse_argv(command);
let p = new Shell.Process({'args' : args});
p.run();
@ -168,7 +170,7 @@ RunDialog.prototype = {
* We are only interested in the actual error, so parse that out.
*/
let m = /.+\((.+)\)/.exec(e);
let errorStr = "Execution of '" + command + "' failed:\n" + m[1];
let errorStr = _("Execution of '%s' failed:").format(command) + "\n" + m[1];
this._errorMessage.set_text(errorStr);
this._errorBox.show();
}
@ -182,6 +184,12 @@ RunDialog.prototype = {
if (!Main.pushModal(this._group))
return;
// Position the dialog on the current monitor
let monitor = global.get_focus_monitor();
this._boxH.set_position(monitor.x, monitor.y);
this._boxH.set_size(monitor.width, monitor.height);
this._isOpen = true;
this._group.show();

View File

@ -274,7 +274,7 @@ WindowManager.prototype = {
_startAppSwitcher : function(shellwm, binding, window, backwards) {
let tabPopup = new AltTab.AltTabPopup();
if (!tabPopup.show(backwards ? -1 : 1))
if (!tabPopup.show(backwards))
tabPopup.destroy();
}
};

View File

@ -111,6 +111,8 @@ WindowClone.prototype = {
this.origX = realWindow.x;
this.origY = realWindow.y;
this._stackAbove = null;
this._title = null;
this.actor.connect('button-release-event',
@ -129,6 +131,8 @@ WindowClone.prototype = {
this._draggable.connect('drag-begin', Lang.bind(this, this._onDragBegin));
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
this._inDrag = false;
this._zooming = false;
},
setVisibleWithChrome: function(visible) {
@ -143,6 +147,14 @@ WindowClone.prototype = {
}
},
setStackAbove: function (actor) {
this._stackAbove = actor;
if (this._inDrag || this._zooming)
// We'll fix up the stack after the drag/zooming
return;
this.actor.raise(this._stackAbove);
},
destroy: function () {
this.actor.destroy();
if (this._title)
@ -157,7 +169,6 @@ WindowClone.prototype = {
this._havePointer = true;
actor.raise_top();
this._updateTitle();
},
@ -206,6 +217,8 @@ WindowClone.prototype = {
},
_zoomStart : function () {
this._zooming = true;
this._zoomLightbox = new Lightbox.Lightbox(global.stage);
this._zoomLocalOrig = new ScaledPoint(this.actor.x, this.actor.y, this.actor.scale_x, this.actor.scale_y);
@ -232,7 +245,10 @@ WindowClone.prototype = {
},
_zoomEnd : function () {
this._zooming = false;
this.actor.reparent(this._origParent);
this.actor.raise(this._stackAbove);
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
[this.actor.scale_x, this.actor.scale_y] = this._zoomLocalOrig.getScale();
@ -272,6 +288,12 @@ WindowClone.prototype = {
// mysteriously present
this._havePointer = false;
// We may not have a parent if DnD completed successfully, in
// which case our clone will shortly be destroyed and replaced
// with a new one on the target workspace.
if (this.actor.get_parent() != null)
this.actor.raise(this._stackAbove);
this.emit('drag-end');
},
@ -283,7 +305,6 @@ WindowClone.prototype = {
// Called by Tweener
onAnimationComplete : function () {
this._updateTitle();
this.actor.raise(this.stackAbove);
},
_createTitle : function () {
@ -618,12 +639,43 @@ Workspace.prototype = {
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
},
_isCloneVisible: function(clone) {
return this._showOnlyWindows == null || (clone.metaWindow in this._showOnlyWindows);
},
/**
* _getVisibleClones:
*
* Returns a list WindowClone objects where the clone isn't filtered
* out by any application filter. The clone for the desktop is excluded.
* The returned array will always be newly allocated; it is not in any
* defined order, and thus it's convenient to call .sort() with your
* choice of sorting function.
*/
_getVisibleClones: function() {
let visible = [];
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
if (!this._isCloneVisible(clone))
continue;
visible.push(clone);
}
return visible;
},
_getVisibleWindows: function() {
return this._getVisibleClones().map(function (clone) { return clone.metaWindow; });
},
_resetCloneVisibility: function () {
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
let icon = this._windowIcons[i];
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) {
if (!this._isCloneVisible(clone)) {
clone.setVisibleWithChrome(false);
icon.hide();
} else {
@ -853,21 +905,12 @@ Workspace.prototype = {
positionWindows : function(workspaceZooming) {
let totalVisible = 0;
let visibleWindows = [];
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
continue;
visibleWindows.push(clone.metaWindow);
}
let visibleWindows = this._getVisibleWindows();
// Start the animations
let slots = this._computeAllWindowSlots(visibleWindows.length);
visibleWindows = this._orderWindowsByMotionAndStartup(visibleWindows, slots);
let previousWindow = this._windows[0];
for (let i = 0; i < visibleWindows.length; i++) {
let slot = slots[i];
let metaWindow = visibleWindows[i];
@ -875,9 +918,6 @@ Workspace.prototype = {
let clone = metaWindow._delegate;
let icon = this._windowIcons[mainIndex];
clone.stackAbove = previousWindow.actor;
previousWindow = clone;
let [x, y, scale] = this._computeWindowRelativeLayout(metaWindow, slot);
icon.hide();
@ -896,6 +936,24 @@ Workspace.prototype = {
}
},
syncStacking: function(stackIndices) {
let desktopClone = this._windows[0];
let visibleClones = this._getVisibleClones();
visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
for (let i = 0; i < visibleClones.length; i++) {
let clone = visibleClones[i];
let metaWindow = clone.metaWindow;
if (i == 0) {
clone.setStackAbove(desktopClone.actor);
} else {
let previousClone = visibleClones[i - 1];
clone.setStackAbove(previousClone.actor);
}
}
},
_fadeInWindowIcon: function (clone, icon) {
icon.opacity = 0;
icon.show();
@ -1320,10 +1378,12 @@ Workspaces.prototype = {
// workspaces have been created. This cannot be done first because
// window movement depends on the Workspaces object being accessible
// as an Overview member.
Main.overview.connect('showing',
Lang.bind(this, function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
this._overviewShowingId =
Main.overview.connect('showing',
Lang.bind(this, function() {
this._onRestacked();
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverview();
}));
// Track changes to the number of workspaces
@ -1333,6 +1393,9 @@ Workspaces.prototype = {
this._switchWorkspaceNotifyId =
global.window_manager.connect('switch-workspace',
Lang.bind(this, this._activeWorkspaceChanged));
this._restackedNotifyId =
global.screen.connect('restacked',
Lang.bind(this, this._onRestacked));
},
_lookupWorkspaceForMetaWindow: function (metaWindow) {
@ -1422,9 +1485,6 @@ Workspaces.prototype = {
this._clearApplicationWindowSelection(false);
}
let clone = this._lookupCloneForMetaWindow (metaWindow);
clone.actor.raise_top();
Main.activateWindow(metaWindow, time);
Main.overview.hide();
},
@ -1448,8 +1508,10 @@ Workspaces.prototype = {
this.actor.destroy();
this.actor = null;
Main.overview.disconnect(this._overviewShowingId);
global.screen.disconnect(this._nWorkspacesNotifyId);
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
global.screen.disconnect(this._restackedNotifyId);
},
getScale : function() {
@ -1595,6 +1657,19 @@ Workspaces.prototype = {
this.actor.add_actor(workspace.actor);
},
_onRestacked: function() {
let stack = global.get_windows();
let stackIndices = {};
for (let i = 0; i < stack.length; i++) {
// Use the stable sequence for an integer to use as a hash key
stackIndices[stack[i].get_meta_window().get_stable_sequence()] = i;
}
for (let i = 0; i < this._workspaces.length; i++)
this._workspaces[i].syncStacking(stackIndices);
},
// Handles a drop onto the (+) button; assumes the new workspace
// has already been added
acceptNewWorkspaceDrop : function(source, dropActor, x, y, time) {

View File

@ -6,14 +6,14 @@ msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-09-25 04:22+0200\n"
"PO-Revision-Date: 2009-09-25 04:21+0300\n"
"POT-Creation-Date: 2009-10-03 19:19+0200\n"
"PO-Revision-Date: 2009-10-03 19:20+0300\n"
"Last-Translator: Khaled Hosny <khaledhosny@eglug.org>\n"
"Language-Team: Arabic <doc@arabeyes.org>\n"
"Language: ar\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ar\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
"X-Generator: Virtaal 0.4.0\n"
@ -27,12 +27,12 @@ msgid "Window management and application launching"
msgstr "إدارة النوافذ وإطلاق التطبيقات"
#. left side
#: ../js/ui/panel.js:269
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "الأنشطة"
#. Translators: This is a time format.
#: ../js/ui/panel.js:452
#: ../js/ui/panel.js:461
msgid "%a %l:%M %p"
msgstr "%A %Ol:%OM %p"
@ -41,48 +41,48 @@ msgid "Find..."
msgstr "ابحث..."
#: ../js/ui/dash.js:400
msgid "Browse"
msgstr "استعرض"
msgid "More"
msgstr "المزيد"
#: ../js/ui/dash.js:536
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(انظر الكل)"
#. **** Applications ****
#: ../js/ui/dash.js:753 ../js/ui/dash.js:809
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
msgid "APPLICATIONS"
msgstr "التطبيقات"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:773
#: ../js/ui/dash.js:783
msgid "PLACES"
msgstr "الأماكن"
#. **** Documents ****
#: ../js/ui/dash.js:780 ../js/ui/dash.js:819
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
msgid "RECENT DOCUMENTS"
msgstr "المستندات الحديثة"
#. **** Search Results ****
#: ../js/ui/dash.js:799 ../js/ui/dash.js:931
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
msgid "SEARCH RESULTS"
msgstr "نتائج البحث"
#: ../js/ui/dash.js:814
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "التفضيلات"
#: ../js/ui/runDialog.js:95
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "من فضلك اكتب أمرا:"
#: ../src/shell-global.c:799
#: ../src/shell-global.c:812
msgid "Less than a minute ago"
msgstr "منذ أقل من دقيقة"
#: ../src/shell-global.c:802
#: ../src/shell-global.c:815
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
@ -93,7 +93,7 @@ msgstr[3] "منذ %d دقائق"
msgstr[4] "منذ %d دقيقة"
msgstr[5] "منذ %d دقيقة"
#: ../src/shell-global.c:805
#: ../src/shell-global.c:818
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
@ -104,7 +104,7 @@ msgstr[3] "منذ %d ساعات"
msgstr[4] "منذ %d ساعة"
msgstr[5] "منذ %d ساعة"
#: ../src/shell-global.c:808
#: ../src/shell-global.c:821
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
@ -115,7 +115,7 @@ msgstr[3] "منذ %d أيام"
msgstr[4] "منذ %d يوما"
msgstr[5] "منذ %d يوم"
#: ../src/shell-global.c:811
#: ../src/shell-global.c:824
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -199,3 +199,6 @@ msgstr "ابحث"
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Browse"
#~ msgstr "استعرض"

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-30 18:53+0200\n"
"PO-Revision-Date: 2009-08-30 18:57+0100\n"
"POT-Creation-Date: 2009-10-01 23:16+0200\n"
"PO-Revision-Date: 2009-10-01 23:20+0100\n"
"Last-Translator: Siegfried-Angel Gevatter Pujals <rainct@ubuntu.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@ -30,76 +30,81 @@ msgid "Activities"
msgstr "Activitats"
#. Translators: This is a time format.
#: ../js/ui/panel.js:454
#: ../js/ui/panel.js:461
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/dash.js:256
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Cerca..."
#: ../js/ui/dash.js:374
msgid "Browse"
msgstr "Navega"
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Més"
#: ../js/ui/dash.js:451
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(mostra tot)"
#. **** Applications ****
#: ../js/ui/dash.js:633
#: ../js/ui/dash.js:681
#: ../js/ui/dash.js:763
#: ../js/ui/dash.js:825
msgid "APPLICATIONS"
msgstr "APLICACIONS"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:653
#: ../js/ui/dash.js:783
msgid "PLACES"
msgstr "LLOCS"
#. **** Documents ****
#: ../js/ui/dash.js:660
#: ../js/ui/dash.js:692
#: ../js/ui/dash.js:790
#: ../js/ui/dash.js:835
msgid "RECENT DOCUMENTS"
msgstr "DOCUMENTS RECENTS"
#. **** Search Results ****
#: ../js/ui/dash.js:679
#: ../js/ui/dash.js:815
#: ../js/ui/dash.js:958
msgid "SEARCH RESULTS"
msgstr "RESULTATS DE LA CERCA"
#: ../js/ui/runDialog.js:82
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "PREFERÈNCIES"
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Introduïu una ordre:"
#: ../src/shell-global.c:840
#: ../src/shell-global.c:812
msgid "Less than a minute ago"
msgstr "Fa menys d'un minut"
#: ../src/shell-global.c:843
#: ../src/shell-global.c:815
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "Fa %d minut"
msgstr[1] "Fa %d minuts"
#: ../src/shell-global.c:846
#: ../src/shell-global.c:818
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "Fa %d hora"
msgstr[1] "Fa %d hores"
#: ../src/shell-global.c:849
#: ../src/shell-global.c:821
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "Fa %d dia"
msgstr[1] "Fa %d dies"
#: ../src/shell-global.c:852
#: ../src/shell-global.c:824
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -180,10 +185,3 @@ msgstr "Cerca"
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Find apps or documents"
#~ msgstr "Cerca aplicacions o documents"
#~ msgid "Manager"
#~ msgstr "Gestor"
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "L'objecte gestor d'usuaris que controla a aquest usuari."

View File

@ -4,18 +4,20 @@
#
# Hendrik Brandt <heb@gnome-de.org>, 2009.
# Hendrik Richter <hendrikr@gnome.org>, 2009.
# Christian Kirbach <Christian.Kirbach@googlemail.com>, 2009.
#
msgid ""
msgstr ""
"Project-Id-Version: HEAD\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-20 16:47+0200\n"
"PO-Revision-Date: 2009-08-20 16:49+0200\n"
"Last-Translator: Hendrik Richter <hendrikr@gnome.org>\n"
"Language-Team: Deutsch <gnome-de@gnome.org>\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
"shell&component=general\n"
"POT-Creation-Date: 2009-10-01 22:22+0000\n"
"PO-Revision-Date: 2009-10-01 14:32+0200\n"
"Last-Translator: Christian Kirbach <Christian.Kirbach@googlemail.com>\n"
"Language-Team: German <gnome-de@gnome.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: ../data/gnome-shell.desktop.in.in.h:1
@ -27,78 +29,83 @@ msgid "Window management and application launching"
msgstr "Fenster verwalten und Anwendungen starten"
#. left side
#: ../js/ui/panel.js:266
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Aktivitäten"
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
#: ../js/ui/panel.js:461
msgid "%a %l:%M %p"
msgstr "%a %H:%M"
#: ../js/ui/dash.js:251
msgid "Find apps or documents"
msgstr "Anwendungen oder Dokumente suchen"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Suchen"
#: ../js/ui/dash.js:369
msgid "Browse"
msgstr "Durchsuchen"
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Mehr"
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(alle sehen)"
#. **** Applications ****
#: ../js/ui/dash.js:505 ../js/ui/dash.js:578
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
msgid "APPLICATIONS"
msgstr "ANWENDUNGEN"
#. **** Documents ****
#: ../js/ui/dash.js:510 ../js/ui/dash.js:605
msgid "RECENT DOCUMENTS"
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:598
#: ../js/ui/dash.js:783
msgid "PLACES"
msgstr "ORTE"
#: ../js/ui/runDialog.js:75
#. **** Documents ****
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
msgid "RECENT DOCUMENTS"
msgstr "ZULETZT GEÖFFNETE DOKUMENTE"
#. **** Search Results ****
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
msgid "SEARCH RESULTS"
msgstr "SUCHERGEBNISSE"
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "EINSTELLUNGEN"
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Bitte geben Sie einen Befehl ein:"
#: ../src/gdmuser/gdm-user.c:243
msgid "Manager"
msgstr "Verwaltung"
#: ../src/gdmuser/gdm-user.c:244
msgid "The user manager object this user is controlled by."
msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."
#: ../src/shell-global.c:841
#: ../src/shell-global.c:812
msgid "Less than a minute ago"
msgstr "Vor weniger als einer Minute"
#: ../src/shell-global.c:844
#: ../src/shell-global.c:815
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "Vor %d Minute"
msgstr[1] "Vor %d Minuten"
#: ../src/shell-global.c:847
#: ../src/shell-global.c:818
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "Vor %d Stunde"
msgstr[1] "Vor %d Stunden"
#: ../src/shell-global.c:850
#: ../src/shell-global.c:821
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "Vor %d Tag"
msgstr[1] "Vor %d Tagen"
#: ../src/shell-global.c:853
#: ../src/shell-global.c:824
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"
@ -180,3 +187,15 @@ msgstr "Suchen"
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#~ msgid "Find apps or documents"
#~ msgstr "Anwendungen oder Dokumente suchen"
#~ msgid "Browse"
#~ msgstr "Durchsuchen"
#~ msgid "Manager"
#~ msgstr "Verwaltung"
#~ msgid "The user manager object this user is controlled by."
#~ msgstr "Das Benutzerverwaltungsobjekt welches diesen Benutzer überwacht."

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell 0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-21 12:36+0200\n"
"PO-Revision-Date: 2009-08-21 12:44+0200\n"
"POT-Creation-Date: 2009-10-03 10:47+0200\n"
"PO-Revision-Date: 2009-10-03 10:48+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@broadpark.no>\n"
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
"MIME-Version: 1.0\n"
@ -25,70 +25,83 @@ msgid "Window management and application launching"
msgstr "Vindushåndtering og oppstart av programmer"
#. left side
#: ../js/ui/panel.js:266
#: ../js/ui/panel.js:271
msgid "Activities"
msgstr "Aktiviteter"
#. Translators: This is a time format.
#: ../js/ui/panel.js:433
#: ../js/ui/panel.js:461
msgid "%a %l:%M %p"
msgstr "%a %l:%M"
#: ../js/ui/dash.js:250
msgid "Find apps or documents"
msgstr "Finn programmer eller dokumenter"
#: ../js/ui/dash.js:283
msgid "Find..."
msgstr "Finn..."
#: ../js/ui/dash.js:368
msgid "Browse"
msgstr "Bla gjennom"
#: ../js/ui/dash.js:400
msgid "More"
msgstr "Mer"
#: ../js/ui/dash.js:543
msgid "(see all)"
msgstr "(se alle)"
#. **** Applications ****
#: ../js/ui/dash.js:504 ../js/ui/dash.js:577
#: ../js/ui/dash.js:763 ../js/ui/dash.js:825
msgid "APPLICATIONS"
msgstr "PROGRAMMER"
#. **** Documents ****
#: ../js/ui/dash.js:509 ../js/ui/dash.js:604
msgid "RECENT DOCUMENTS"
msgstr "SISTE DOKUMENTER"
#. **** Places ****
#. Translators: This is in the sense of locations for documents,
#. network locations, etc.
#: ../js/ui/dash.js:597
#: ../js/ui/dash.js:783
msgid "PLACES"
msgstr "STEDER"
#: ../js/ui/runDialog.js:75
#. **** Documents ****
#: ../js/ui/dash.js:790 ../js/ui/dash.js:835
msgid "RECENT DOCUMENTS"
msgstr "SISTE DOKUMENTER"
#. **** Search Results ****
#: ../js/ui/dash.js:815 ../js/ui/dash.js:955
msgid "SEARCH RESULTS"
msgstr "SØKERESULTATER"
#: ../js/ui/dash.js:830
msgid "PREFERENCES"
msgstr "BRUKERVALG"
#: ../js/ui/runDialog.js:96
msgid "Please enter a command:"
msgstr "Oppgi en kommando:"
#: ../src/shell-global.c:841
#: ../src/shell-global.c:812
msgid "Less than a minute ago"
msgstr "Mindre enn ett minutt siden"
#: ../src/shell-global.c:844
#: ../src/shell-global.c:815
#, c-format
msgid "%d minute ago"
msgid_plural "%d minutes ago"
msgstr[0] "%d minutt siden"
msgstr[1] "%d minutter siden"
#: ../src/shell-global.c:847
#: ../src/shell-global.c:818
#, c-format
msgid "%d hour ago"
msgid_plural "%d hours ago"
msgstr[0] "%d time siden"
msgstr[1] "%d timer siden"
#: ../src/shell-global.c:850
#: ../src/shell-global.c:821
#, c-format
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dag siden"
msgstr[1] "%d dager siden"
#: ../src/shell-global.c:853
#: ../src/shell-global.c:824
#, c-format
msgid "%d week ago"
msgid_plural "%d weeks ago"

148
src/Makefile-st.am Normal file
View File

@ -0,0 +1,148 @@
st_cflags = \
-I$(top_srcdir)/src \
-DPREFIX=\""$(prefix)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DG_DISABLE_DEPRECATED \
-DG_LOG_DOMAIN=\"St\" \
-DST_COMPILATION \
-DPACKAGE_DATA_DIR=\"$(pkgdatadir)\" \
$(ST_CFLAGS) \
$(NULL)
st_built_sources = \
st-enum-types.h \
st-enum-types.c \
st-marshal.h \
st-marshal.c
BUILT_SOURCES += $(st_built_sources)
EXTRA_DIST += \
st/test-theme.css \
st/st-marshal.list \
st/st-enum-types.h.in \
st/st-enum-types.c.in
CLEANFILES += stamp-st-marshal.h stamp-st-enum-types.h
st-marshal.h: stamp-st-marshal.h
@true
stamp-st-marshal.h: Makefile st/st-marshal.list
$(AM_V_GEN) $(GLIB_GENMARSHAL) \
--prefix=_st_marshal \
--header \
$(srcdir)/st/st-marshal.list > $@.tmp && \
(cmp -s $@.tmp st-marshal.h || cp -f $@.tmp st-marshal.h) && \
rm -f $@.tmp && \
echo timestamp > $(@F)
st-marshal.c: Makefile st/st-marshal.list
$(AM_V_GEN) (echo "#include \"st-marshal.h\"" ; \
$(GLIB_GENMARSHAL) \
--prefix=_st_marshal \
--body \
$(srcdir)/st/st-marshal.list ) > $@.tmp && \
cp -f $@.tmp st-marshal.c && \
rm -f $@.tmp
st-enum-types.h: stamp-st-enum-types.h Makefile
@true
stamp-st-enum-types.h: $(source_h) st/st-enum-types.h.in
$(AM_V_GEN) ( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template st/st-enum-types.h.in \
$(st_source_h) ) >> $@.tmp && \
(cmp -s $@.tmp st-enum-types.h || cp $@.tmp st-enum-types.h) && \
rm -f $@.tmp && \
echo timestamp > $(@F)
st-enum-types.c: stamp-st-enum-types.h st/st-enum-types.c.in
$(AM_V_GEN) ( cd $(srcdir) && \
$(GLIB_MKENUMS) \
--template st/st-enum-types.c.in \
$(st_source_h) ) >> $@.tmp && \
cp $@.tmp $@ && \
rm -f $@.tmp
# please, keep this sorted alphabetically
st_source_h = \
st/st-adjustment.h \
st/st-bin.h \
st/st-border-image.h \
st/st-box-layout.h \
st/st-box-layout-child.h \
st/st-button.h \
st/st-clipboard.h \
st/st-entry.h \
st/st-im-text.h \
st/st-label.h \
st/st-private.h \
st/st-scrollable.h \
st/st-scroll-bar.h \
st/st-scroll-view.h \
st/st-subtexture.h \
st/st-table.h \
st/st-table-child.h \
st/st-table-private.h \
st/st-texture-cache.h \
st/st-texture-frame.h \
st/st-theme.h \
st/st-theme-context.h \
st/st-theme-node.h \
st/st-theme-private.h \
st/st-tooltip.h \
st/st-types.h \
st/st-widget.h \
$(NULL)
st_source_private_h = \
st/st-private.h \
st/st-table-private.h \
st/st-theme-private.h
# please, keep this sorted alphabetically
st_source_c = \
st/st-adjustment.c \
st/st-bin.c \
st/st-border-image.c \
st/st-box-layout.c \
st/st-box-layout-child.c \
st/st-button.c \
st/st-clipboard.c \
st/st-entry.c \
st/st-im-text.c \
st/st-label.c \
st/st-private.c \
st/st-scrollable.c \
st/st-scroll-bar.c \
st/st-scroll-view.c \
st/st-subtexture.c \
st/st-table.c \
st/st-table-child.c \
st/st-texture-cache.c \
st/st-texture-frame.c \
st/st-theme.c \
st/st-theme-context.c \
st/st-theme-node.c \
st/st-tooltip.c \
st/st-widget.c \
$(NULL)
noinst_LTLIBRARIES += libst-1.0.la
libst_1_0_la_LIBADD = $(ST_LIBS)
libst_1_0_la_SOURCES = \
$(st_source_c) \
$(st_source_private_c) \
$(st_source_h) \
$(st_built_sources) \
$(NULL)
libst_1_0_la_CPPFLAGS = $(st_cflags)
libst_1_0_la_LDFLAGS = $(LDADD)
noinst_PROGRAMS += test-theme
test_theme_CPPFLAGS = $(st_cflags)
test_theme_LDADD = libst-1.0.la
test_theme_SOURCES = st/test-theme.c

View File

@ -4,6 +4,7 @@ CLEANFILES =
EXTRA_DIST =
libexec_PROGRAMS =
noinst_LTLIBRARIES =
noinst_PROGRAMS =
.AUTOPARALLEL:
@ -24,6 +25,7 @@ EXTRA_DIST += gnome-shell.in
include Makefile-big.am
include Makefile-gdmuser.am
include Makefile-st.am
include Makefile-tray.am
gnome_shell_cflags = \
@ -94,7 +96,7 @@ libgnome_shell_la_SOURCES = \
shell-wm.c \
shell-wm.h
non_gir_sources = \
non_gir_sources = \
shell-embedded-window-private.h
shell_recorder_sources = \
@ -112,7 +114,7 @@ if BUILD_RECORDER
libgnome_shell_la_SOURCES += $(shell_recorder_sources)
non_gir_sources += $(shell_recorder_non_gir_sources)
noinst_PROGRAMS = test-recorder
noinst_PROGRAMS += test-recorder
test_recorder_CPPFLAGS = $(TEST_SHELL_RECORDER_CFLAGS)
test_recorder_LDADD = $(TEST_SHELL_RECORDER_LIBS)
@ -150,23 +152,25 @@ libgnome_shell_la_LIBADD = \
$(MUTTER_PLUGIN_LIBS) \
$(LIBGNOMEUI_LIBS) \
libbig-1.0.la \
libst-1.0.la \
libgdmuser-1.0.la \
libtray.la
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
typelibdir = $(pkglibdir)
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib
typelib_DATA = Shell-0.1.typelib Big-1.0.typelib St-1.0.typelib
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir libgnome-shell.la Makefile
Shell-0.1.gir: $(mutter) $(G_IR_SCANNER) Big-1.0.gir St-1.0.gir libgnome-shell.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Shell \
--nsversion=0.1 \
--add-include-path=$(MUTTER_LIB_DIR)/mutter/ \
--include=Clutter-1.0 \
--include=Meta-2.27 \
--include=Meta-2.28 \
--libtool="$(LIBTOOL)" \
--add-include-path=$(builddir) \
--include=Big-1.0 \
--include=St-1.0 \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
$(addprefix $(srcdir)/,$(libgnome_shell_la_gir_sources)) \
@ -177,14 +181,14 @@ CLEANFILES += Shell-0.1.gir
# The dependency on libgnome-shell.la here is because g-ir-compiler opens it
# (not the fake library, since we've already done the rewriting)
Shell-0.1.typelib: libgnome-shell.la Shell-0.1.gir Big-1.0.gir
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
$(AM_V_GEN) \
$(G_IR_COMPILER) \
--includedir=. \
--includedir=$(MUTTER_LIB_DIR)/mutter/ \
Shell-0.1.gir -o $@
CLEANFILES += Shell-0.1.typelib
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)/big-enum-types.h Makefile
Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=Big \
--nsversion=1.0 \
@ -201,6 +205,28 @@ Big-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libbig-1.0.la $(srcdir)
CLEANFILES += Big-1.0.gir
Big-1.0.typelib: libbig-1.0.la Big-1.0.gir
$(AM_V_GEN) LD_LIBRARY_PATH=.$${LD_LIBRARY_PATH:+:$$LD_LIBRARY_PATH} \
$(G_IR_COMPILER) Big-1.0.gir -o $@
$(AM_V_GEN) $(G_IR_COMPILER) Big-1.0.gir -o $@
CLEANFILES += Big-1.0.typelib
St-1.0.gir: $(mutter) $(G_IR_SCANNER) libgnome-shell.la libst-1.0.la Makefile
$(AM_V_GEN) $(G_IR_SCANNER) \
--namespace=St \
--nsversion=1.0 \
--include=Clutter-1.0 \
--add-include-path=$(builddir) \
--libtool="$(LIBTOOL)" \
--program=mutter \
--program-arg=--mutter-plugins=$$(pwd)/libgnome-shell.la \
-DST_COMPILATION \
$(addprefix $(srcdir)/,$(st_source_h)) \
$(addprefix $(srcdir)/,$(st_source_c)) \
$(srcdir)/st-enum-types.h \
$(st_cflags) \
-o $@
CLEANFILES += St-1.0.gir
St-1.0.typelib: St-1.0.gir
$(AM_V_GEN) $(G_IR_COMPILER) \
$< -o $@
CLEANFILES += St-1.0.typelib

View File

@ -268,6 +268,52 @@ corner_get(guint radius,
return corner;
}
/* To match the CSS specification, we want the border to look like it was
* drawn over the background. But actually drawing the border over the
* background will produce slightly bad antialiasing at the edges, so
* compute the effective border color instead.
*/
#define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
#define MULT(c,a) NORM(c*a)
static void
premultiply (ClutterColor *color)
{
guint t;
color->red = MULT (color->red, color->alpha);
color->green = MULT (color->green, color->alpha);
color->blue = MULT (color->blue, color->alpha);
}
static void
unpremultiply (ClutterColor *color)
{
if (color->alpha != 0) {
color->red = (color->red * 255 + 127) / color->alpha;
color->green = (color->green * 255 + 127) / color->alpha;
color->blue = (color->blue * 255 + 127) / color->alpha;
}
}
static void
over (const ClutterColor *source,
const ClutterColor *destination,
ClutterColor *result)
{
guint t;
ClutterColor src = *source;
ClutterColor dst = *destination;
premultiply (&src);
premultiply (&dst);
result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha);
result->red = src.red + NORM ((255 - src.alpha) * dst.red);
result->green = src.green + NORM ((255 - src.alpha) * dst.green);
result->blue = src.blue + NORM ((255 - src.alpha) * dst.blue);
unpremultiply (result);
}
static void
big_rectangle_update_corners(BigRectangle *rectangle)
{
@ -278,6 +324,7 @@ big_rectangle_update_corners(BigRectangle *rectangle)
if (rectangle->radius != 0) {
ClutterColor *color;
ClutterColor *border_color;
ClutterColor effective_border;
guint border_width;
g_object_get(rectangle,
@ -286,10 +333,12 @@ big_rectangle_update_corners(BigRectangle *rectangle)
"color", &color,
NULL);
over (border_color, color, &effective_border);
corner = corner_get(rectangle->radius,
color,
border_width,
border_color);
&effective_border);
clutter_color_free(border_color);
clutter_color_free(color);
@ -329,12 +378,10 @@ big_rectangle_paint(ClutterActor *actor)
rectangle = BIG_RECTANGLE(actor);
if (rectangle->radius == 0) {
/* In that case we are no different than our parent class,
* so don't bother */
CLUTTER_ACTOR_CLASS(big_rectangle_parent_class)->paint(actor);
return;
}
/* We can't chain up, even when we the radius is 0, because of the different
* interpretation of the border/background relationship here than for
* ClutterRectangle.
*/
if (rectangle->corners_dirty)
big_rectangle_update_corners(rectangle);
@ -345,6 +392,9 @@ big_rectangle_paint(ClutterActor *actor)
"color", &color,
NULL);
if (border_color->alpha == 0 && color->alpha == 0)
goto out;
actor_opacity = clutter_actor_get_paint_opacity (actor);
clutter_actor_get_allocation_box(actor, &box);
@ -358,6 +408,11 @@ big_rectangle_paint(ClutterActor *actor)
radius = rectangle->radius;
/* Optimization; if the border is transparent, it just looks like part of
* the background */
if (radius == 0 && border_color->alpha == 0)
border_width = 0;
max = MAX(border_width, radius);
if (radius != 0) {
@ -393,33 +448,54 @@ big_rectangle_paint(ClutterActor *actor)
}
if (border_width != 0) {
ClutterColor effective_border;
over (border_color, color, &effective_border);
if (!rectangle->border_material)
rectangle->border_material = cogl_material_new ();
cogl_color_set_from_4ub(&tmp_color,
border_color->red,
border_color->green,
border_color->blue,
actor_opacity * border_color->alpha / 255);
effective_border.red,
effective_border.green,
effective_border.blue,
actor_opacity * effective_border.alpha / 255);
cogl_color_premultiply (&tmp_color);
cogl_material_set_color(rectangle->border_material, &tmp_color);
cogl_set_source(rectangle->border_material);
/* NORTH */
cogl_rectangle(max, 0,
width - max, border_width);
if (radius > 0) { /* skip corners */
/* NORTH */
cogl_rectangle(max, 0,
width - max, border_width);
/* EAST */
cogl_rectangle(width - border_width, max,
width, height - max);
/* EAST */
cogl_rectangle(width - border_width, max,
width, height - max);
/* SOUTH */
cogl_rectangle(max, height - border_width,
width - max, height);
/* SOUTH */
cogl_rectangle(max, height - border_width,
width - max, height);
/* WEST */
cogl_rectangle(0, max,
border_width, height - max);
/* WEST */
cogl_rectangle(0, max,
border_width, height - max);
} else { /* include corners */
/* NORTH */
cogl_rectangle(0, 0,
width, border_width);
/* EAST */
cogl_rectangle(width - border_width, border_width,
width, height - border_width);
/* SOUTH */
cogl_rectangle(0, height - border_width,
width, height);
/* WEST */
cogl_rectangle(0, border_width,
border_width, height - border_width);
}
}
if (!rectangle->background_material)
@ -455,6 +531,7 @@ big_rectangle_paint(ClutterActor *actor)
cogl_rectangle(border_width, max,
width - border_width, height - max);
out:
clutter_color_free(border_color);
clutter_color_free(color);
}

View File

@ -1,64 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include <mutter-plugin.h>
ClutterActor *
mutter_plugin_get_overlay_group (MutterPlugin *plugin)
{
return NULL;
}
ClutterActor *
mutter_plugin_get_stage (MutterPlugin *plugin)
{
return NULL;
}
GList *
mutter_plugin_get_windows (MutterPlugin *plugin)
{
return NULL;
}
void
mutter_plugin_query_screen_size (MutterPlugin *plugin,
int *width,
int *height)
{
}
void
mutter_plugin_set_stage_input_area (MutterPlugin *plugin,
gint x, gint y, gint width, gint height)
{
}
MetaScreen *
mutter_plugin_get_screen (MutterPlugin *plugin)
{
return NULL;
}
ClutterActor *
mutter_plugin_get_window_group (MutterPlugin *plugin)
{
return NULL;
}
Display *
meta_display_get_xdisplay (MetaDisplay *display)
{
return NULL;
}
MetaDisplay *
meta_screen_get_display (MetaScreen *display)
{
return NULL;
}
Window
meta_screen_get_xroot (MetaScreen *display)
{
return None;
}

View File

@ -45,6 +45,7 @@ struct _ShellAppSystemPrivate {
GHashTable *app_id_to_app;
GHashTable *cached_menu_contents; /* <char *id, GSList<ShellAppInfo*>> */
GSList *cached_app_menus; /* ShellAppMenuEntry */
GSList *cached_settings; /* ShellAppInfo */
@ -56,6 +57,7 @@ struct _ShellAppSystemPrivate {
guint app_change_timeout_id;
};
static void free_appinfo_gslist (gpointer list);
static void shell_app_system_finalize (GObject *object);
static gboolean on_tree_changed (gpointer user_data);
static void on_tree_changed_cb (GMenuTree *tree, gpointer user_data);
@ -229,6 +231,9 @@ shell_app_system_init (ShellAppSystem *self)
priv->app_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL, (GDestroyNotify) shell_app_info_unref);
priv->cached_menu_contents = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, free_appinfo_gslist);
/* For now, we want to pick up Evince, Nautilus, etc. We'll
* handle NODISPLAY semantics at a higher level or investigate them
* case by case.
@ -262,6 +267,8 @@ shell_app_system_finalize (GObject *object)
gmenu_tree_unref (priv->apps_tree);
gmenu_tree_unref (priv->settings_tree);
g_hash_table_destroy (priv->cached_menu_contents);
g_hash_table_destroy (priv->app_id_to_app);
g_slist_foreach (priv->cached_app_menus, (GFunc)shell_app_menu_entry_free, NULL);
@ -279,6 +286,14 @@ shell_app_system_finalize (GObject *object)
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
}
static void
free_appinfo_gslist (gpointer listp)
{
GSList *list = listp;
g_slist_foreach (list, (GFunc) shell_app_info_unref, NULL);
g_slist_free (list);
}
static void
reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
{
@ -419,8 +434,12 @@ static gboolean
on_tree_changed (gpointer user_data)
{
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
reread_menus (self);
g_hash_table_remove_all (self->priv->cached_menu_contents);
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
self->priv->app_change_timeout_id = 0;
return FALSE;
}
@ -537,26 +556,32 @@ shell_app_menu_entry_get_type (void)
* shell_app_system_get_applications_for_menu:
*
* Traverses a toplevel menu, and returns all items under it. Nested items
* are flattened.
* are flattened. This value is computed on initial call and cached thereafter
* until the set of installed applications changes.
*
* Return value: (transfer full) (element-type ShellAppInfo): List of applications
* Return value: (transfer none) (element-type ShellAppInfo): List of applications
*/
GSList *
shell_app_system_get_applications_for_menu (ShellAppSystem *monitor,
shell_app_system_get_applications_for_menu (ShellAppSystem *self,
const char *menu)
{
char *path;
GMenuTreeDirectory *menu_entry;
GSList *apps;
path = g_strdup_printf ("/%s", menu);
menu_entry = gmenu_tree_get_directory_from_path (monitor->priv->apps_tree, path);
g_free (path);
g_assert (menu_entry != NULL);
apps = g_hash_table_lookup (self->priv->cached_menu_contents, menu);
if (!apps)
{
char *path;
GMenuTreeDirectory *menu_entry;
path = g_strdup_printf ("/%s", menu);
menu_entry = gmenu_tree_get_directory_from_path (self->priv->apps_tree, path);
g_free (path);
g_assert (menu_entry != NULL);
apps = gather_entries_recurse (monitor, NULL, menu_entry);
apps = gather_entries_recurse (self, NULL, menu_entry);
g_hash_table_insert (self->priv->cached_menu_contents, g_strdup (menu), apps);
gmenu_tree_item_unref (menu_entry);
gmenu_tree_item_unref (menu_entry);
}
return apps;
}

View File

@ -147,17 +147,14 @@ shell_draw_clock (ClutterCairoTexture *texture,
}
void
shell_draw_box_pointer (ClutterCairoTexture *texture,
ClutterGravity pointing_towards,
ClutterColor *border_color,
ClutterColor *background_color)
shell_draw_box_pointer (ClutterCairoTexture *texture,
ShellPointerDirection direction,
ClutterColor *border_color,
ClutterColor *background_color)
{
guint width, height;
cairo_t *cr;
g_return_if_fail (pointing_towards == CLUTTER_GRAVITY_NORTH ||
pointing_towards == CLUTTER_GRAVITY_WEST);
clutter_cairo_texture_get_surface_size (texture, &width, &height);
clutter_cairo_texture_clear (texture);
@ -167,17 +164,34 @@ shell_draw_box_pointer (ClutterCairoTexture *texture,
clutter_cairo_set_source_color (cr, border_color);
if (pointing_towards == CLUTTER_GRAVITY_WEST)
{
cairo_move_to (cr, width, 0);
cairo_line_to (cr, 0, floor (height * 0.5));
cairo_line_to (cr, width, height);
}
else /* CLUTTER_GRAVITY_NORTH */
switch (direction)
{
case SHELL_POINTER_UP:
cairo_move_to (cr, 0, height);
cairo_line_to (cr, floor (width * 0.5), 0);
cairo_line_to (cr, width, height);
break;
case SHELL_POINTER_DOWN:
cairo_move_to (cr, width, 0);
cairo_line_to (cr, floor (width * 0.5), height);
cairo_line_to (cr, 0, 0);
break;
case SHELL_POINTER_LEFT:
cairo_move_to (cr, width, height);
cairo_line_to (cr, 0, floor (height * 0.5));
cairo_line_to (cr, width, 0);
break;
case SHELL_POINTER_RIGHT:
cairo_move_to (cr, 0, 0);
cairo_line_to (cr, width, floor (height * 0.5));
cairo_line_to (cr, 0, height);
break;
default:
g_assert_not_reached();
}
cairo_stroke_preserve (cr);

View File

@ -13,10 +13,17 @@ ClutterCairoTexture *shell_create_vertical_gradient (ClutterColor *top,
ClutterCairoTexture *shell_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right);
void shell_draw_box_pointer (ClutterCairoTexture *texture,
ClutterGravity pointing_towards,
ClutterColor *border_color,
ClutterColor *background_color);
typedef enum {
SHELL_POINTER_UP,
SHELL_POINTER_DOWN,
SHELL_POINTER_LEFT,
SHELL_POINTER_RIGHT
} ShellPointerDirection;
void shell_draw_box_pointer (ClutterCairoTexture *texture,
ShellPointerDirection direction,
ClutterColor *border_color,
ClutterColor *background_color);
void shell_draw_clock (ClutterCairoTexture *texture,
int hour,

View File

@ -40,6 +40,7 @@ struct _ShellGlobal {
MutterPlugin *plugin;
ShellWM *wm;
const char *datadir;
const char *imagedir;
const char *configdir;
@ -57,6 +58,7 @@ enum {
PROP_STAGE,
PROP_WINDOW_GROUP,
PROP_WINDOW_MANAGER,
PROP_DATADIR,
PROP_IMAGEDIR,
PROP_CONFIGDIR,
};
@ -128,6 +130,9 @@ shell_global_get_property(GObject *object,
case PROP_WINDOW_MANAGER:
g_value_set_object (value, global->wm);
break;
case PROP_DATADIR:
g_value_set_string (value, global->datadir);
break;
case PROP_IMAGEDIR:
g_value_set_string (value, global->imagedir);
break;
@ -149,6 +154,7 @@ shell_global_init (ShellGlobal *global)
if (!datadir)
datadir = GNOME_SHELL_DATADIR;
global->datadir = datadir;
/* We make sure imagedir ends with a '/', since the JS won't have
* access to g_build_filename() and so will end up just
@ -254,6 +260,13 @@ shell_global_class_init (ShellGlobalClass *klass)
"Window management interface",
SHELL_TYPE_WM,
G_PARAM_READABLE));
g_object_class_install_property (gobject_class,
PROP_DATADIR,
g_param_spec_string ("datadir",
"Data directory",
"Directory containing gnome-shell data files",
NULL,
G_PARAM_READABLE));
g_object_class_install_property (gobject_class,
PROP_IMAGEDIR,
g_param_spec_string ("imagedir",
@ -869,3 +882,138 @@ shell_global_create_root_pixmap_actor (ShellGlobal *global)
return clutter_clone_new (global->root_pixmap);
}
/**
* shell_global_get_monitors:
* @global: the #ShellGlobal
*
* Gets a list of the bounding boxes of the active screen's monitors.
*
* Return value: (transfer full) (element-type GdkRectangle): a list
* of monitor bounding boxes.
*/
GSList *
shell_global_get_monitors (ShellGlobal *global)
{
MetaScreen *screen = shell_global_get_screen (global);
GSList *monitors = NULL;
MetaRectangle rect;
int i;
g_assert (sizeof (MetaRectangle) == sizeof (GdkRectangle) &&
G_STRUCT_OFFSET (MetaRectangle, x) == G_STRUCT_OFFSET (GdkRectangle, x) &&
G_STRUCT_OFFSET (MetaRectangle, y) == G_STRUCT_OFFSET (GdkRectangle, y) &&
G_STRUCT_OFFSET (MetaRectangle, width) == G_STRUCT_OFFSET (GdkRectangle, width) &&
G_STRUCT_OFFSET (MetaRectangle, height) == G_STRUCT_OFFSET (GdkRectangle, height));
for (i = meta_screen_get_n_monitors (screen) - 1; i >= 0; i--)
{
meta_screen_get_monitor_geometry (screen, i, &rect);
monitors = g_slist_prepend (monitors,
g_boxed_copy (GDK_TYPE_RECTANGLE, &rect));
}
return monitors;
}
/**
* shell_global_get_primary_monitor:
* @global: the #ShellGlobal
*
* Gets the bounding box of the primary monitor (the one that the
* panel is on).
*
* Return value: the bounding box of the primary monitor
*/
GdkRectangle *
shell_global_get_primary_monitor (ShellGlobal *global)
{
MetaScreen *screen = shell_global_get_screen (global);
MetaRectangle rect;
g_assert (sizeof (MetaRectangle) == sizeof (GdkRectangle) &&
G_STRUCT_OFFSET (MetaRectangle, x) == G_STRUCT_OFFSET (GdkRectangle, x) &&
G_STRUCT_OFFSET (MetaRectangle, y) == G_STRUCT_OFFSET (GdkRectangle, y) &&
G_STRUCT_OFFSET (MetaRectangle, width) == G_STRUCT_OFFSET (GdkRectangle, width) &&
G_STRUCT_OFFSET (MetaRectangle, height) == G_STRUCT_OFFSET (GdkRectangle, height));
meta_screen_get_monitor_geometry (screen, 0, &rect);
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
}
/**
* shell_global_get_focus_monitor:
* @global: the #ShellGlobal
*
* Gets the bounding box of the monitor containing the window that
* currently contains the keyboard focus.
*
* Return value: the bounding box of the focus monitor
*/
GdkRectangle *
shell_global_get_focus_monitor (ShellGlobal *global)
{
MetaScreen *screen = shell_global_get_screen (global);
MetaDisplay *display = meta_screen_get_display (screen);
MetaWindow *focus = meta_display_get_focus_window (display);
MetaRectangle rect, wrect;
int nmonitors, i;
if (focus)
{
meta_window_get_outer_rect (focus, &wrect);
nmonitors = meta_screen_get_n_monitors (screen);
/* Find the monitor that the top-left corner of @focus is on. */
for (i = 0; i < nmonitors; i++)
{
meta_screen_get_monitor_geometry (screen, i, &rect);
if (rect.x < wrect.x && rect.y < wrect.y &&
rect.x + rect.width > wrect.x &&
rect.y + rect.height > wrect.y)
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
}
}
meta_screen_get_monitor_geometry (screen, 0, &rect);
return g_boxed_copy (GDK_TYPE_RECTANGLE, &rect);
}
/**
* shell_global_get_modifier_keys:
* @global: the #ShellGlobal
*
* Gets the current set of modifier keys that are pressed down;
* this is a wrapper around gdk_display_get_pointer() that strips
* out any un-declared modifier flags, to make gjs happy; see
* https://bugzilla.gnome.org/show_bug.cgi?id=597292.
*
* Return value: the current modifiers
*/
GdkModifierType
shell_global_get_modifier_keys (ShellGlobal *global)
{
GdkModifierType mods;
gdk_display_get_pointer (gdk_display_get_default (), NULL, NULL, NULL, &mods);
return mods & GDK_MODIFIER_MASK;
}
/**
* shell_get_event_state:
* @event: a #ClutterEvent
*
* Gets the current state of the event (the set of modifier keys that
* are pressed down). Thhis is a wrapper around
* clutter_event_get_state() that strips out any un-declared modifier
* flags, to make gjs happy; see
* https://bugzilla.gnome.org/show_bug.cgi?id=597292.
*
* Return value: the state from the event
*/
ClutterModifierType
shell_get_event_state (ClutterEvent *event)
{
ClutterModifierType state = clutter_event_get_state (event);
return state & CLUTTER_MODIFIER_MASK;
}

View File

@ -73,6 +73,14 @@ void shell_global_format_time_relative_pretty (ShellGlobal *global, guint delta,
ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global);
GSList *shell_global_get_monitors (ShellGlobal *global);
GdkRectangle *shell_global_get_primary_monitor (ShellGlobal *global);
GdkRectangle *shell_global_get_focus_monitor (ShellGlobal *global);
GdkModifierType shell_global_get_modifier_keys (ShellGlobal *global);
ClutterModifierType shell_get_event_state (ClutterEvent *event);
G_END_DECLS
#endif /* __SHELL_GLOBAL_H__ */

View File

@ -15,6 +15,7 @@ G_DEFINE_TYPE(ShellMenu, shell_menu, BIG_TYPE_BOX);
struct _ShellMenuPrivate {
gboolean popped_up;
gboolean have_grab;
guint activating_button;
gboolean released_on_source;
ClutterActor *source_actor;
@ -117,9 +118,15 @@ shell_menu_button_release_event (ClutterActor *actor,
{
ShellMenu *box = SHELL_MENU (actor);
if (event->button != 1)
/* Until the user releases the button that brought up the menu, we just
* ignore other button press/releass.
* See https://bugzilla.gnome.org/show_bug.cgi?id=596371
*/
if (box->priv->activating_button > 0 && box->priv->activating_button != event->button)
return FALSE;
box->priv->activating_button = 0;
if (box->priv->source_actor && !box->priv->released_on_source)
{
if (box->priv->source_actor == event->source ||
@ -134,13 +141,8 @@ shell_menu_button_release_event (ClutterActor *actor,
shell_menu_popdown_nosignal (box);
if (!container_contains (CLUTTER_CONTAINER (box), event->source))
{
g_signal_emit (G_OBJECT (box), shell_menu_signals[CANCELLED], 0);
return FALSE;
}
if (box->priv->selected == NULL)
if (!container_contains (CLUTTER_CONTAINER (box), event->source) ||
box->priv->selected == NULL)
{
g_signal_emit (G_OBJECT (box), shell_menu_signals[CANCELLED], 0);
return FALSE;
@ -158,6 +160,7 @@ shell_menu_popup (ShellMenu *box,
{
if (box->priv->popped_up)
return;
box->priv->activating_button = button;
box->priv->popped_up = TRUE;
box->priv->have_grab = TRUE;
box->priv->released_on_source = FALSE;

View File

@ -1,240 +0,0 @@
#include "shell-panel-window.h"
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#define PANEL_HEIGHT 25
enum {
PROP_0,
};
static void shell_panel_window_finalize (GObject *object);
static void shell_panel_window_size_request (GtkWidget *self, GtkRequisition *req);
static void shell_panel_window_size_allocate (GtkWidget *self, GtkAllocation *allocation);
static void shell_panel_window_realize (GtkWidget *self);
static void shell_panel_window_show (GtkWidget *self);
static void set_strut (ShellPanelWindow *self);
static void on_workarea_changed (ShellPanelWindow *self);
static void handle_new_workarea (ShellPanelWindow *self);
static GdkFilterReturn filter_func (GdkXEvent *xevent,
GdkEvent *event,
gpointer data);
G_DEFINE_TYPE(ShellPanelWindow, shell_panel_window, GTK_TYPE_WINDOW);
struct ShellPanelWindowPrivate {
GtkAllocation workarea;
guint width;
guint height;
Atom workarea_atom;
};
static void
shell_panel_window_class_init(ShellPanelWindowClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *)klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
gobject_class->finalize = shell_panel_window_finalize;
widget_class->realize = shell_panel_window_realize;
widget_class->size_request = shell_panel_window_size_request;
widget_class->size_allocate = shell_panel_window_size_allocate;
widget_class->show = shell_panel_window_show;
}
static void shell_panel_window_init (ShellPanelWindow *self)
{
self->priv = g_new0 (ShellPanelWindowPrivate, 1);
self->priv->workarea_atom = gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (), "_NET_WORKAREA");
gtk_window_set_type_hint (GTK_WINDOW (self), GDK_WINDOW_TYPE_HINT_DOCK);
gtk_window_set_focus_on_map (GTK_WINDOW (self), FALSE);
gdk_window_add_filter (NULL, filter_func, self);
}
static void shell_panel_window_finalize (GObject *object)
{
ShellPanelWindow *self = (ShellPanelWindow*)object;
g_free (self->priv);
g_signal_handlers_destroy(object);
G_OBJECT_CLASS (shell_panel_window_parent_class)->finalize(object);
}
ShellPanelWindow* shell_panel_window_new(void) {
return (ShellPanelWindow*) g_object_new(SHELL_TYPE_PANEL_WINDOW,
"type", GTK_WINDOW_TOPLEVEL, NULL);
}
static void
set_strut (ShellPanelWindow *self)
{
long *buf;
int strut_size;
strut_size = GTK_WIDGET (self)->allocation.height;
buf = g_new0 (long, 4);
buf[0] = 0; /* left */
buf[1] = 0; /* right */
buf[2] = 0; /* top */
buf[3] = strut_size; /* bottom */
gdk_property_change (GTK_WIDGET (self)->window, gdk_atom_intern_static_string ("_NET_WM_STRUT"),
gdk_atom_intern_static_string ("CARDINAL"), 32,
GDK_PROP_MODE_REPLACE,
(guchar*) buf, 4);
g_free (buf);
}
static void
shell_panel_window_size_request (GtkWidget *widget, GtkRequisition *requisition)
{
ShellPanelWindow *self = SHELL_PANEL_WINDOW (widget);
GTK_WIDGET_CLASS (shell_panel_window_parent_class)->size_request(widget, requisition);
requisition->width = self->priv->width;
requisition->height = PANEL_HEIGHT;
}
static void
shell_panel_window_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
ShellPanelWindow *self = SHELL_PANEL_WINDOW (widget);
GTK_WIDGET_CLASS (shell_panel_window_parent_class)->size_allocate(widget, allocation);
if (GTK_WIDGET_REALIZED (self))
set_strut (self);
}
static void
shell_panel_window_realize (GtkWidget *widget)
{
ShellPanelWindow *self = SHELL_PANEL_WINDOW (widget);
GTK_WIDGET_CLASS (shell_panel_window_parent_class)->realize(widget);
set_strut (self);
}
static void
shell_panel_window_show (GtkWidget *widget)
{
ShellPanelWindow *self = SHELL_PANEL_WINDOW (widget);
on_workarea_changed (self);
GTK_WIDGET_CLASS (shell_panel_window_parent_class)->show(widget);
}
static void
handle_new_workarea (ShellPanelWindow *self)
{
GtkRequisition requisition;
int x, y;
int width;
int height;
int x_target, y_target;
gtk_widget_size_request (GTK_WIDGET (self), &requisition);
/* If we don't have a workarea, just use monitor */
if (self->priv->workarea.width == 0)
{
int monitor;
GdkRectangle monitor_geometry;
monitor = gdk_screen_get_monitor_at_point (gdk_screen_get_default (),
0, 0);
gdk_screen_get_monitor_geometry (gdk_screen_get_default (),
monitor, &monitor_geometry);
x = monitor_geometry.x;
y = monitor_geometry.y;
width = monitor_geometry.width;
height = monitor_geometry.height;
}
else
{
x = self->priv->workarea.x;
y = self->priv->workarea.y;
width = self->priv->workarea.width;
height = self->priv->workarea.height;
}
x_target = x;
y_target = y + height - requisition.height;
self->priv->width = width;
self->priv->height = height;
gtk_widget_set_size_request (GTK_WIDGET (self), width - x_target, PANEL_HEIGHT);
gtk_window_move (GTK_WINDOW (self), x_target, y_target);
}
static void
on_workarea_changed (ShellPanelWindow *self)
{
gulong bytes_after, nitems;
Atom type;
gint format;
guchar *data;
long *data32;
Atom workarea = gdk_x11_get_xatom_by_name_for_display (gdk_display_get_default (), "_NET_WORKAREA");
XGetWindowProperty (GDK_DISPLAY(), GDK_ROOT_WINDOW(),
workarea,
0, 4, FALSE, workarea,
&type, &format, &nitems, &bytes_after, &data);
if ((format == 32) && (nitems == 4) && (bytes_after == 0))
{
int x, y, width, height;
data32 = (long*) data;
x = data32[0];
y = data32[1];
width = data32[2];
height = data32[3];
if (x == self->priv->workarea.x && y == self->priv->workarea.y
&& width == self->priv->workarea.width
&& height == self->priv->workarea.height)
return;
self->priv->workarea.x = x;
self->priv->workarea.y = y;
self->priv->workarea.width = width;
self->priv->workarea.height = height;
handle_new_workarea (self);
}
else if (nitems == 0)
{
/* We have no workarea set; assume there are no other panels at this time */
self->priv->workarea.x = self->priv->workarea.y = 0;
self->priv->workarea.width = self->priv->workarea.height = 0;
handle_new_workarea (self);
}
else
{
g_printerr ("unexpected return from XGetWindowProperty: %d %ld %ld\n",
format, nitems, bytes_after);
}
}
static GdkFilterReturn
filter_func (GdkXEvent *gdk_xevent,
GdkEvent *event,
gpointer data)
{
ShellPanelWindow *self = SHELL_PANEL_WINDOW (data);
GdkFilterReturn ret = GDK_FILTER_CONTINUE;
XEvent *xevent = (XEvent *) event;
switch (xevent->type)
{
case PropertyNotify:
{
if (xevent->xproperty.atom != self->priv->workarea_atom)
break;
on_workarea_changed (self);
}
break;
default:
break;
}
return ret;
}

View File

@ -1,35 +0,0 @@
#ifndef __SHELL_PANEL_WINDOW_H__
#define __SHELL_PANEL_WINDOW_H__
#include <gtk/gtk.h>
#define SHELL_TYPE_PANEL_WINDOW (shell_panel_window_get_type ())
#define SHELL_PANEL_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_PANEL_WINDOW, ShellPanelWindow))
#define SHELL_PANEL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_PANEL_WINDOW, ShellPanelWindowClass))
#define SHELL_IS_PANEL_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_PANEL_WINDOW))
#define SHELL_IS_PANEL_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_PANEL_WINDOW))
#define SHELL_PANEL_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_PANEL_WINDOW, ShellPanelWindowClass))
typedef struct _ShellPanelWindow ShellPanelWindow;
typedef struct _ShellPanelWindowClass ShellPanelWindowClass;
typedef struct ShellPanelWindowPrivate ShellPanelWindowPrivate;
struct _ShellPanelWindow
{
GtkWindow parent;
ShellPanelWindowPrivate *priv;
};
struct _ShellPanelWindowClass
{
GtkWindowClass parent_class;
};
GType shell_panel_window_get_type (void) G_GNUC_CONST;
ShellPanelWindow* shell_panel_window_new(void);
#endif /* __SHELL_PANEL_WINDOW_H__ */

777
src/st/st-adjustment.c Normal file
View File

@ -0,0 +1,777 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-adjustment.c: Adjustment object
*
* Copyright (C) 2008 OpenedHand
* Copyright (c) 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Chris Lord <chris@openedhand.com>, inspired by GtkAdjustment
* Port to St by: Robert Staudinger <robsta@openedhand.com>
*
*/
/**
* SECTION:st-adjustment
* @short_description: A GObject representing an adjustable bounded value
*
* The #StAdjustment object represents a range of values bounded between a
* minimum and maximum, together with step and page increments and a page size.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib-object.h>
#include <clutter/clutter.h>
#include "st-adjustment.h"
#include "st-marshal.h"
#include "st-private.h"
G_DEFINE_TYPE (StAdjustment, st_adjustment, G_TYPE_OBJECT)
#define ADJUSTMENT_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_ADJUSTMENT, StAdjustmentPrivate))
struct _StAdjustmentPrivate
{
/* Do not sanity-check values while constructing,
* not all properties may be set yet. */
gboolean is_constructing : 1;
gdouble lower;
gdouble upper;
gdouble value;
gdouble step_increment;
gdouble page_increment;
gdouble page_size;
/* For interpolation */
ClutterTimeline *interpolation;
gdouble old_position;
gdouble new_position;
/* For elasticity */
gboolean elastic;
guint bounce_source;
ClutterAlpha *bounce_alpha;
};
enum
{
PROP_0,
PROP_LOWER,
PROP_UPPER,
PROP_VALUE,
PROP_STEP_INC,
PROP_PAGE_INC,
PROP_PAGE_SIZE,
PROP_ELASTIC,
};
enum
{
CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0, };
static gboolean st_adjustment_set_lower (StAdjustment *adjustment,
gdouble lower);
static gboolean st_adjustment_set_upper (StAdjustment *adjustment,
gdouble upper);
static gboolean st_adjustment_set_step_increment (StAdjustment *adjustment,
gdouble step);
static gboolean st_adjustment_set_page_increment (StAdjustment *adjustment,
gdouble page);
static gboolean st_adjustment_set_page_size (StAdjustment *adjustment,
gdouble size);
static void
st_adjustment_constructed (GObject *object)
{
GObjectClass *g_class;
StAdjustment *self = ST_ADJUSTMENT (object);
g_class = G_OBJECT_CLASS (st_adjustment_parent_class);
/* The docs say we're suppose to chain up, but would crash without
* some extra care. */
if (g_class && g_class->constructed &&
g_class->constructed != st_adjustment_constructed)
{
g_class->constructed (object);
}
ST_ADJUSTMENT (self)->priv->is_constructing = FALSE;
st_adjustment_clamp_page (self, self->priv->lower, self->priv->upper);
}
static void
st_adjustment_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StAdjustmentPrivate *priv = ST_ADJUSTMENT (gobject)->priv;
switch (prop_id)
{
case PROP_LOWER:
g_value_set_double (value, priv->lower);
break;
case PROP_UPPER:
g_value_set_double (value, priv->upper);
break;
case PROP_VALUE:
g_value_set_double (value, priv->value);
break;
case PROP_STEP_INC:
g_value_set_double (value, priv->step_increment);
break;
case PROP_PAGE_INC:
g_value_set_double (value, priv->page_increment);
break;
case PROP_PAGE_SIZE:
g_value_set_double (value, priv->page_size);
break;
case PROP_ELASTIC:
g_value_set_boolean (value, priv->elastic);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_adjustment_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StAdjustment *adj = ST_ADJUSTMENT (gobject);
switch (prop_id)
{
case PROP_LOWER:
st_adjustment_set_lower (adj, g_value_get_double (value));
break;
case PROP_UPPER:
st_adjustment_set_upper (adj, g_value_get_double (value));
break;
case PROP_VALUE:
st_adjustment_set_value (adj, g_value_get_double (value));
break;
case PROP_STEP_INC:
st_adjustment_set_step_increment (adj, g_value_get_double (value));
break;
case PROP_PAGE_INC:
st_adjustment_set_page_increment (adj, g_value_get_double (value));
break;
case PROP_PAGE_SIZE:
st_adjustment_set_page_size (adj, g_value_get_double (value));
break;
case PROP_ELASTIC:
st_adjustment_set_elastic (adj, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
stop_interpolation (StAdjustment *adjustment)
{
StAdjustmentPrivate *priv = adjustment->priv;
if (priv->interpolation)
{
clutter_timeline_stop (priv->interpolation);
g_object_unref (priv->interpolation);
priv->interpolation = NULL;
if (priv->bounce_alpha)
{
g_object_unref (priv->bounce_alpha);
priv->bounce_alpha = NULL;
}
}
if (priv->bounce_source)
{
g_source_remove (priv->bounce_source);
priv->bounce_source = 0;
}
}
static void
st_adjustment_dispose (GObject *object)
{
stop_interpolation (ST_ADJUSTMENT (object));
G_OBJECT_CLASS (st_adjustment_parent_class)->dispose (object);
}
static void
st_adjustment_class_init (StAdjustmentClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (StAdjustmentPrivate));
object_class->constructed = st_adjustment_constructed;
object_class->get_property = st_adjustment_get_property;
object_class->set_property = st_adjustment_set_property;
object_class->dispose = st_adjustment_dispose;
g_object_class_install_property (object_class,
PROP_LOWER,
g_param_spec_double ("lower",
"Lower",
"Lower bound",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_UPPER,
g_param_spec_double ("upper",
"Upper",
"Upper bound",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_VALUE,
g_param_spec_double ("value",
"Value",
"Current value",
-G_MAXDOUBLE,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_STEP_INC,
g_param_spec_double ("step-increment",
"Step Increment",
"Step increment",
0.0,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_PAGE_INC,
g_param_spec_double ("page-increment",
"Page Increment",
"Page increment",
0.0,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_PAGE_SIZE,
g_param_spec_double ("page-size",
"Page Size",
"Page size",
0.0,
G_MAXDOUBLE,
0.0,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (object_class,
PROP_ELASTIC,
g_param_spec_boolean ("elastic",
"Elastic",
"Make interpolation "
"behave in an "
"'elastic' way and "
"stop clamping value.",
FALSE,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
/**
* StAdjustment::changed:
*
* Emitted when any of the adjustment values have changed
*/
signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (StAdjustmentClass, changed),
NULL, NULL,
_st_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
st_adjustment_init (StAdjustment *self)
{
self->priv = ADJUSTMENT_PRIVATE (self);
self->priv->is_constructing = TRUE;
}
StAdjustment *
st_adjustment_new (gdouble value,
gdouble lower,
gdouble upper,
gdouble step_increment,
gdouble page_increment,
gdouble page_size)
{
return g_object_new (ST_TYPE_ADJUSTMENT,
"value", value,
"lower", lower,
"upper", upper,
"step-increment", step_increment,
"page-increment", page_increment,
"page-size", page_size,
NULL);
}
gdouble
st_adjustment_get_value (StAdjustment *adjustment)
{
StAdjustmentPrivate *priv;
g_return_val_if_fail (ST_IS_ADJUSTMENT (adjustment), 0);
priv = adjustment->priv;
if (priv->interpolation)
{
return MAX (priv->lower,
MIN (priv->upper - priv->page_size,
priv->new_position));
}
else
return priv->value;
}
void
st_adjustment_set_value (StAdjustment *adjustment,
gdouble value)
{
StAdjustmentPrivate *priv;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
priv = adjustment->priv;
stop_interpolation (adjustment);
/* Defer clamp until after construction. */
if (!priv->is_constructing)
{
if (!priv->elastic)
value = CLAMP (value,
priv->lower,
MAX (priv->lower, priv->upper - priv->page_size));
}
if (priv->value != value)
{
priv->value = value;
g_object_notify (G_OBJECT (adjustment), "value");
}
}
void
st_adjustment_clamp_page (StAdjustment *adjustment,
gdouble lower,
gdouble upper)
{
StAdjustmentPrivate *priv;
gboolean changed;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
priv = adjustment->priv;
stop_interpolation (adjustment);
lower = CLAMP (lower, priv->lower, priv->upper - priv->page_size);
upper = CLAMP (upper, priv->lower + priv->page_size, priv->upper);
changed = FALSE;
if (priv->value + priv->page_size > upper)
{
priv->value = upper - priv->page_size;
changed = TRUE;
}
if (priv->value < lower)
{
priv->value = lower;
changed = TRUE;
}
if (changed)
g_object_notify (G_OBJECT (adjustment), "value");
}
static gboolean
st_adjustment_set_lower (StAdjustment *adjustment,
gdouble lower)
{
StAdjustmentPrivate *priv = adjustment->priv;
if (priv->lower != lower)
{
priv->lower = lower;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "lower");
/* Defer clamp until after construction. */
if (!priv->is_constructing)
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
return TRUE;
}
return FALSE;
}
static gboolean
st_adjustment_set_upper (StAdjustment *adjustment,
gdouble upper)
{
StAdjustmentPrivate *priv = adjustment->priv;
if (priv->upper != upper)
{
priv->upper = upper;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "upper");
/* Defer clamp until after construction. */
if (!priv->is_constructing)
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
return TRUE;
}
return FALSE;
}
static gboolean
st_adjustment_set_step_increment (StAdjustment *adjustment,
gdouble step)
{
StAdjustmentPrivate *priv = adjustment->priv;
if (priv->step_increment != step)
{
priv->step_increment = step;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "step-increment");
return TRUE;
}
return FALSE;
}
static gboolean
st_adjustment_set_page_increment (StAdjustment *adjustment,
gdouble page)
{
StAdjustmentPrivate *priv = adjustment->priv;
if (priv->page_increment != page)
{
priv->page_increment = page;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "page-increment");
return TRUE;
}
return FALSE;
}
static gboolean
st_adjustment_set_page_size (StAdjustment *adjustment,
gdouble size)
{
StAdjustmentPrivate *priv = adjustment->priv;
if (priv->page_size != size)
{
priv->page_size = size;
g_signal_emit (adjustment, signals[CHANGED], 0);
g_object_notify (G_OBJECT (adjustment), "page_size");
/* Well explicitely clamp after construction. */
if (!priv->is_constructing)
st_adjustment_clamp_page (adjustment, priv->lower, priv->upper);
return TRUE;
}
return FALSE;
}
void
st_adjustment_set_values (StAdjustment *adjustment,
gdouble value,
gdouble lower,
gdouble upper,
gdouble step_increment,
gdouble page_increment,
gdouble page_size)
{
StAdjustmentPrivate *priv;
gboolean emit_changed = FALSE;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
g_return_if_fail (page_size >= 0 && page_size <= G_MAXDOUBLE);
g_return_if_fail (step_increment >= 0 && step_increment <= G_MAXDOUBLE);
g_return_if_fail (page_increment >= 0 && page_increment <= G_MAXDOUBLE);
priv = adjustment->priv;
stop_interpolation (adjustment);
emit_changed = FALSE;
g_object_freeze_notify (G_OBJECT (adjustment));
emit_changed |= st_adjustment_set_lower (adjustment, lower);
emit_changed |= st_adjustment_set_upper (adjustment, upper);
emit_changed |= st_adjustment_set_step_increment (adjustment, step_increment);
emit_changed |= st_adjustment_set_page_increment (adjustment, page_increment);
emit_changed |= st_adjustment_set_page_size (adjustment, page_size);
if (value != priv->value)
{
st_adjustment_set_value (adjustment, value);
emit_changed = TRUE;
}
if (emit_changed)
g_signal_emit (G_OBJECT (adjustment), signals[CHANGED], 0);
g_object_thaw_notify (G_OBJECT (adjustment));
}
void
st_adjustment_get_values (StAdjustment *adjustment,
gdouble *value,
gdouble *lower,
gdouble *upper,
gdouble *step_increment,
gdouble *page_increment,
gdouble *page_size)
{
StAdjustmentPrivate *priv;
g_return_if_fail (ST_IS_ADJUSTMENT (adjustment));
priv = adjustment->priv;
if (lower)
*lower = priv->lower;
if (upper)
*upper = priv->upper;
if (value)
*value = st_adjustment_get_value (adjustment);
if (step_increment)
*step_increment = priv->step_increment;
if (page_increment)
*page_increment = priv->page_increment;
if (page_size)
*page_size = priv->page_size;
}
static void
interpolation_new_frame_cb (ClutterTimeline *timeline,
guint msecs,
StAdjustment *adjustment)
{
StAdjustmentPrivate *priv = adjustment->priv;
priv->interpolation = NULL;
if (priv->elastic)
{
gdouble progress = clutter_alpha_get_alpha (priv->bounce_alpha) / 1.0;
gdouble dx = priv->old_position
+ (priv->new_position - priv->old_position)
* progress;
st_adjustment_set_value (adjustment, dx);
}
else
st_adjustment_set_value (adjustment,
priv->old_position +
(priv->new_position - priv->old_position) *
clutter_timeline_get_progress (timeline));
priv->interpolation = timeline;
}
static void
interpolation_completed_cb (ClutterTimeline *timeline,
StAdjustment *adjustment)
{
StAdjustmentPrivate *priv = adjustment->priv;
stop_interpolation (adjustment);
st_adjustment_set_value (adjustment, priv->new_position);
}
/* Note, there's super-optimal code that does a similar thing in
* clutter-alpha.c
*
* Tried this instead of CLUTTER_ALPHA_SINE_INC, but I think SINE_INC looks
* better. Leaving code here in case this is revisited.
*/
/*
static guint32
bounce_alpha_func (ClutterAlpha *alpha,
gpointer user_data)
{
ClutterFixed progress, angle;
ClutterTimeline *timeline = clutter_alpha_get_timeline (alpha);
progress = clutter_timeline_get_progressx (timeline);
angle = clutter_qmulx (CFX_PI_2 + CFX_PI_4/2, progress);
return clutter_sinx (angle) +
(CFX_ONE - clutter_sinx (CFX_PI_2 + CFX_PI_4/2));
}
*/
void
st_adjustment_interpolate (StAdjustment *adjustment,
gdouble value,
guint duration)
{
StAdjustmentPrivate *priv = adjustment->priv;
stop_interpolation (adjustment);
if (duration <= 1)
{
st_adjustment_set_value (adjustment, value);
return;
}
priv->old_position = priv->value;
priv->new_position = value;
priv->interpolation = clutter_timeline_new (duration);
if (priv->elastic)
priv->bounce_alpha = clutter_alpha_new_full (priv->interpolation,
CLUTTER_LINEAR);
g_signal_connect (priv->interpolation,
"new-frame",
G_CALLBACK (interpolation_new_frame_cb),
adjustment);
g_signal_connect (priv->interpolation,
"completed",
G_CALLBACK (interpolation_completed_cb),
adjustment);
clutter_timeline_start (priv->interpolation);
}
gboolean
st_adjustment_get_elastic (StAdjustment *adjustment)
{
return adjustment->priv->elastic;
}
void
st_adjustment_set_elastic (StAdjustment *adjustment,
gboolean elastic)
{
adjustment->priv->elastic = elastic;
}
gboolean
st_adjustment_clamp (StAdjustment *adjustment,
gboolean interpolate,
guint duration)
{
StAdjustmentPrivate *priv = adjustment->priv;
gdouble dest = priv->value;
if (priv->value < priv->lower)
dest = priv->lower;
if (priv->value > priv->upper - priv->page_size)
dest = priv->upper - priv->page_size;
if (dest != priv->value)
{
if (interpolate)
st_adjustment_interpolate (adjustment, dest, duration);
else
st_adjustment_set_value (adjustment, dest);
return TRUE;
}
return FALSE;
}

122
src/st/st-adjustment.h Normal file
View File

@ -0,0 +1,122 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-adjustment.h: Adjustment object
*
* Copyright 2008 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Chris Lord <chris@openedhand.com>, inspired by GtkAdjustment
* Port to St by: Robert Staudinger <robsta@openedhand.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_ADJUSTMENT_H__
#define __ST_ADJUSTMENT_H__
#include <glib-object.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_ADJUSTMENT (st_adjustment_get_type())
#define ST_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_ADJUSTMENT, StAdjustment))
#define ST_IS_ADJUSTMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_ADJUSTMENT))
#define ST_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_ADJUSTMENT, StAdjustmentClass))
#define ST_IS_ADJUSTMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_ADJUSTMENT))
#define ST_ADJUSTMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_ADJUSTMENT, StAdjustmentClass))
typedef struct _StAdjustment StAdjustment;
typedef struct _StAdjustmentPrivate StAdjustmentPrivate;
typedef struct _StAdjustmentClass StAdjustmentClass;
/**
* StAdjustment:
*
* Class for handling an interval between to values. The contents of
* the #StAdjustment are private and should be accessed using the
* public API.
*/
struct _StAdjustment
{
/*< private >*/
GObject parent_instance;
StAdjustmentPrivate *priv;
};
/**
* StAdjustmentClass
* @changed: Class handler for the ::changed signal.
*
* Base class for #StAdjustment.
*/
struct _StAdjustmentClass
{
/*< private >*/
GObjectClass parent_class;
/*< public >*/
void (* changed) (StAdjustment *adjustment);
};
GType st_adjustment_get_type (void) G_GNUC_CONST;
StAdjustment *st_adjustment_new (gdouble value,
gdouble lower,
gdouble upper,
gdouble step_increment,
gdouble page_increment,
gdouble page_size);
gdouble st_adjustment_get_value (StAdjustment *adjustment);
void st_adjustment_set_value (StAdjustment *adjustment,
gdouble value);
void st_adjustment_clamp_page (StAdjustment *adjustment,
gdouble lower,
gdouble upper);
void st_adjustment_set_values (StAdjustment *adjustment,
gdouble value,
gdouble lower,
gdouble upper,
gdouble step_increment,
gdouble page_increment,
gdouble page_size);
void st_adjustment_get_values (StAdjustment *adjustment,
gdouble *value,
gdouble *lower,
gdouble *upper,
gdouble *step_increment,
gdouble *page_increment,
gdouble *page_size);
void st_adjustment_interpolate (StAdjustment *adjustment,
gdouble value,
guint duration);
gboolean st_adjustment_get_elastic (StAdjustment *adjustment);
void st_adjustment_set_elastic (StAdjustment *adjustment,
gboolean elastic);
gboolean st_adjustment_clamp (StAdjustment *adjustment,
gboolean interpolate,
guint duration);
G_END_DECLS
#endif /* __ST_ADJUSTMENT_H__ */

748
src/st/st-bin.c Normal file
View File

@ -0,0 +1,748 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-bin.c: Basic container actor
*
* Copyright (c) 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Emmanuele Bassi <ebassi@linux.intel.com>
*
*/
/**
* SECTION:st-bin
* @short_description: a simple container with one actor
*
* #StBin is a simple container capable of having only one
* #ClutterActor as a child.
*
* #StBin inherits from #StWidget, so it is fully themable.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <clutter/clutter.h>
#include "st-bin.h"
#include "st-enum-types.h"
#include "st-private.h"
#define ST_BIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BIN, StBinPrivate))
struct _StBinPrivate
{
ClutterActor *child;
StAlign x_align;
StAlign y_align;
guint x_fill : 1;
guint y_fill : 1;
};
enum
{
PROP_0,
PROP_CHILD,
PROP_X_ALIGN,
PROP_Y_ALIGN,
PROP_X_FILL,
PROP_Y_FILL
};
static void clutter_container_iface_init (ClutterContainerIface *iface);
G_DEFINE_TYPE_WITH_CODE (StBin, st_bin, ST_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
clutter_container_iface_init));
void
_st_bin_get_align_factors (StBin *bin,
gdouble *x_align,
gdouble *y_align)
{
StBinPrivate *priv = bin->priv;
gdouble factor;
switch (priv->x_align)
{
case ST_ALIGN_START:
factor = 0.0;
break;
case ST_ALIGN_MIDDLE:
factor = 0.5;
break;
case ST_ALIGN_END:
factor = 1.0;
break;
default:
factor = 0.0;
break;
}
if (x_align)
*x_align = factor;
switch (priv->y_align)
{
case ST_ALIGN_START:
factor = 0.0;
break;
case ST_ALIGN_MIDDLE:
factor = 0.5;
break;
case ST_ALIGN_END:
factor = 1.0;
break;
default:
factor = 0.0;
break;
}
if (y_align)
*y_align = factor;
}
static void
st_bin_add (ClutterContainer *container,
ClutterActor *actor)
{
st_bin_set_child (ST_BIN (container), actor);
}
static void
st_bin_remove (ClutterContainer *container,
ClutterActor *actor)
{
StBinPrivate *priv = ST_BIN (container)->priv;
if (priv->child == actor)
st_bin_set_child (ST_BIN (container), NULL);
}
static void
st_bin_foreach (ClutterContainer *container,
ClutterCallback callback,
gpointer user_data)
{
StBinPrivate *priv = ST_BIN (container)->priv;
if (priv->child)
callback (priv->child, user_data);
}
static void
clutter_container_iface_init (ClutterContainerIface *iface)
{
iface->add = st_bin_add;
iface->remove = st_bin_remove;
iface->foreach = st_bin_foreach;
}
static void
st_bin_paint (ClutterActor *self)
{
StBinPrivate *priv = ST_BIN (self)->priv;
/* allow StWidget to paint the background */
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->paint (self);
/* the pain our child */
if (priv->child)
clutter_actor_paint (priv->child);
}
static void
st_bin_pick (ClutterActor *self,
const ClutterColor *pick_color)
{
StBinPrivate *priv = ST_BIN (self)->priv;
/* get the default pick implementation */
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->pick (self, pick_color);
if (priv->child)
clutter_actor_paint (priv->child);
}
static void
st_bin_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StBinPrivate *priv = ST_BIN (self)->priv;
CLUTTER_ACTOR_CLASS (st_bin_parent_class)->allocate (self, box,
flags);
if (priv->child)
{
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
gfloat natural_width, natural_height;
gfloat min_width, min_height;
gfloat child_width, child_height;
gfloat available_width, available_height;
ClutterRequestMode request;
ClutterActorBox content_box;
ClutterActorBox allocation = { 0, };
gdouble x_align, y_align;
st_theme_node_get_content_box (theme_node, box, &content_box);
_st_bin_get_align_factors (ST_BIN (self), &x_align, &y_align);
available_width = content_box.x2 - content_box.x1;
available_height = content_box.y2 - content_box.y1;
if (available_width < 0)
available_width = 0;
if (available_height < 0)
available_height = 0;
if (priv->x_fill)
{
allocation.x1 = (int) content_box.x1;
allocation.x2 = (int) content_box.x2;
}
if (priv->y_fill)
{
allocation.y1 = (int) content_box.y1;
allocation.y2 = (int) content_box.y2;
}
/* if we are filling horizontally and vertically then we're done */
if (priv->x_fill && priv->y_fill)
{
clutter_actor_allocate (priv->child, &allocation, flags);
return;
}
request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
g_object_get (G_OBJECT (priv->child), "request-mode", &request, NULL);
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
{
clutter_actor_get_preferred_width (priv->child, available_height,
&min_width,
&natural_width);
child_width = CLAMP (natural_width, min_width, available_width);
clutter_actor_get_preferred_height (priv->child, child_width,
&min_height,
&natural_height);
child_height = CLAMP (natural_height, min_height, available_height);
}
else
{
clutter_actor_get_preferred_height (priv->child, available_width,
&min_height,
&natural_height);
child_height = CLAMP (natural_height, min_height, available_height);
clutter_actor_get_preferred_width (priv->child, child_height,
&min_width,
&natural_width);
child_width = CLAMP (natural_width, min_width, available_width);
}
if (!priv->x_fill)
{
allocation.x1 = content_box.x1 + (int) ((available_width - child_width) * x_align);
allocation.x2 = allocation.x1 + child_width;
}
if (!priv->y_fill)
{
allocation.y1 = content_box.y1 + (int) ((available_height - child_height) * y_align);
allocation.y2 = allocation.y1 + child_height;
}
clutter_actor_allocate (priv->child, &allocation, flags);
}
}
static void
st_bin_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StBinPrivate *priv = ST_BIN (self)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
st_theme_node_adjust_for_height (theme_node, &for_height);
if (priv->child == NULL)
{
if (min_width_p)
*min_width_p = 0;
if (natural_width_p)
*natural_width_p = 0;
}
else
{
clutter_actor_get_preferred_width (priv->child, for_height,
min_width_p,
natural_width_p);
}
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
}
static void
st_bin_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StBinPrivate *priv = ST_BIN (self)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
st_theme_node_adjust_for_width (theme_node, &for_width);
if (priv->child == NULL)
{
if (min_height_p)
*min_height_p = 0;
if (natural_height_p)
*natural_height_p = 0;
}
else
{
clutter_actor_get_preferred_height (priv->child, for_width,
min_height_p,
natural_height_p);
}
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static void
st_bin_dispose (GObject *gobject)
{
StBinPrivate *priv = ST_BIN (gobject)->priv;
if (priv->child)
{
clutter_actor_unparent (priv->child);
priv->child = NULL;
}
G_OBJECT_CLASS (st_bin_parent_class)->dispose (gobject);
}
static void
st_bin_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StBin *bin = ST_BIN (gobject);
switch (prop_id)
{
case PROP_CHILD:
st_bin_set_child (bin, g_value_get_object (value));
break;
case PROP_X_ALIGN:
st_bin_set_alignment (bin,
g_value_get_enum (value),
bin->priv->y_align);
break;
case PROP_Y_ALIGN:
st_bin_set_alignment (bin,
bin->priv->x_align,
g_value_get_enum (value));
break;
case PROP_X_FILL:
st_bin_set_fill (bin,
g_value_get_boolean (value),
bin->priv->y_fill);
break;
case PROP_Y_FILL:
st_bin_set_fill (bin,
bin->priv->y_fill,
g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
st_bin_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StBinPrivate *priv = ST_BIN (gobject)->priv;
switch (prop_id)
{
case PROP_CHILD:
g_value_set_object (value, priv->child);
break;
case PROP_X_FILL:
g_value_set_boolean (value, priv->x_fill);
break;
case PROP_Y_FILL:
g_value_set_boolean (value, priv->y_fill);
break;
case PROP_X_ALIGN:
g_value_set_enum (value, priv->x_align);
break;
case PROP_Y_ALIGN:
g_value_set_enum (value, priv->y_align);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
}
}
static void
st_bin_class_init (StBinClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (StBinPrivate));
gobject_class->set_property = st_bin_set_property;
gobject_class->get_property = st_bin_get_property;
gobject_class->dispose = st_bin_dispose;
actor_class->get_preferred_width = st_bin_get_preferred_width;
actor_class->get_preferred_height = st_bin_get_preferred_height;
actor_class->allocate = st_bin_allocate;
actor_class->paint = st_bin_paint;
actor_class->pick = st_bin_pick;
/**
* StBin:child:
*
* The child #ClutterActor of the #StBin container.
*/
pspec = g_param_spec_object ("child",
"Child",
"The child of the Bin",
CLUTTER_TYPE_ACTOR,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_CHILD, pspec);
/**
* StBin:x-align:
*
* The horizontal alignment of the #StBin child.
*/
pspec = g_param_spec_enum ("x-align",
"X Align",
"The horizontal alignment",
ST_TYPE_ALIGN,
ST_ALIGN_MIDDLE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_X_ALIGN, pspec);
/**
* StBin:y-align:
*
* The vertical alignment of the #StBin child.
*/
pspec = g_param_spec_enum ("y-align",
"Y Align",
"The vertical alignment",
ST_TYPE_ALIGN,
ST_ALIGN_MIDDLE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_Y_ALIGN, pspec);
/**
* StBin:x-fill:
*
* Whether the child should fill the horizontal allocation
*/
pspec = g_param_spec_boolean ("x-fill",
"X Fill",
"Whether the child should fill the "
"horizontal allocation",
FALSE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_X_FILL, pspec);
/**
* StBin:y-fill:
*
* Whether the child should fill the vertical allocation
*/
pspec = g_param_spec_boolean ("y-fill",
"Y Fill",
"Whether the child should fill the "
"vertical allocation",
FALSE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_Y_FILL, pspec);
}
static void
st_bin_init (StBin *bin)
{
bin->priv = ST_BIN_GET_PRIVATE (bin);
bin->priv->x_align = ST_ALIGN_MIDDLE;
bin->priv->y_align = ST_ALIGN_MIDDLE;
}
/**
* st_bin_new:
*
* Creates a new #StBin, a simple container for one child.
*
* Return value: the newly created #StBin actor
*/
StWidget *
st_bin_new (void)
{
return g_object_new (ST_TYPE_BIN, NULL);
}
/**
* st_bin_set_child:
* @bin: a #StBin
* @child: a #ClutterActor, or %NULL
*
* Sets @child as the child of @bin.
*
* If @bin already has a child, the previous child is removed.
*/
void
st_bin_set_child (StBin *bin,
ClutterActor *child)
{
StBinPrivate *priv;
g_return_if_fail (ST_IS_BIN (bin));
g_return_if_fail (child == NULL || CLUTTER_IS_ACTOR (child));
priv = bin->priv;
if (priv->child == child)
return;
if (priv->child)
{
ClutterActor *old_child = priv->child;
g_object_ref (old_child);
priv->child = NULL;
clutter_actor_unparent (old_child);
g_signal_emit_by_name (bin, "actor-removed", old_child);
g_object_unref (old_child);
}
if (child)
{
priv->child = child;
clutter_actor_set_parent (child, CLUTTER_ACTOR (bin));
g_signal_emit_by_name (bin, "actor-added", priv->child);
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
g_object_notify (G_OBJECT (bin), "child");
}
/**
* st_bin_get_child:
* @bin: a #StBin
*
* Retrieves a pointer to the child of @bin.
*
* Return value: (transfer none): a #ClutterActor, or %NULL
*/
ClutterActor *
st_bin_get_child (StBin *bin)
{
g_return_val_if_fail (ST_IS_BIN (bin), NULL);
return bin->priv->child;
}
/**
* st_bin_set_alignment:
* @bin: a #StBin
* @x_align: horizontal alignment
* @y_align: vertical alignment
*
* Sets the horizontal and vertical alignment of the child
* inside a #StBin.
*/
void
st_bin_set_alignment (StBin *bin,
StAlign x_align,
StAlign y_align)
{
StBinPrivate *priv;
gboolean changed = FALSE;
g_return_if_fail (ST_IS_BIN (bin));
priv = bin->priv;
g_object_freeze_notify (G_OBJECT (bin));
if (priv->x_align != x_align)
{
priv->x_align = x_align;
g_object_notify (G_OBJECT (bin), "x-align");
changed = TRUE;
}
if (priv->y_align != y_align)
{
priv->y_align = y_align;
g_object_notify (G_OBJECT (bin), "y-align");
changed = TRUE;
}
if (changed)
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
g_object_thaw_notify (G_OBJECT (bin));
}
/**
* st_bin_get_alignment:
* @bin: a #StBin
* @x_align: return location for the horizontal alignment, or %NULL
* @y_align: return location for the vertical alignment, or %NULL
*
* Retrieves the horizontal and vertical alignment of the child
* inside a #StBin, as set by st_bin_set_alignment().
*/
void
st_bin_get_alignment (StBin *bin,
StAlign *x_align,
StAlign *y_align)
{
StBinPrivate *priv;
g_return_if_fail (ST_IS_BIN (bin));
priv = bin->priv;
if (x_align)
*x_align = priv->x_align;
if (y_align)
*y_align = priv->y_align;
}
/**
* st_bin_set_fill:
* @bin: a #StBin
* @x_fill: %TRUE if the child should fill horizontally the @bin
* @y_fill: %TRUE if the child should fill vertically the @bin
*
* Sets whether the child of @bin should fill out the horizontal
* and/or vertical allocation of the parent
*/
void
st_bin_set_fill (StBin *bin,
gboolean x_fill,
gboolean y_fill)
{
StBinPrivate *priv;
gboolean changed = FALSE;
g_return_if_fail (ST_IS_BIN (bin));
priv = bin->priv;
g_object_freeze_notify (G_OBJECT (bin));
if (priv->x_fill != x_fill)
{
priv->x_fill = x_fill;
changed = TRUE;
g_object_notify (G_OBJECT (bin), "x-fill");
}
if (priv->y_fill != y_fill)
{
priv->y_fill = y_fill;
changed = TRUE;
g_object_notify (G_OBJECT (bin), "y-fill");
}
if (changed)
clutter_actor_queue_relayout (CLUTTER_ACTOR (bin));
g_object_thaw_notify (G_OBJECT (bin));
}
/**
* st_bin_get_fill:
* @bin: a #StBin
* @x_fill: (out): return location for the horizontal fill, or %NULL
* @y_fill: (out): return location for the vertical fill, or %NULL
*
* Retrieves the horizontal and vertical fill settings
*/
void
st_bin_get_fill (StBin *bin,
gboolean *x_fill,
gboolean *y_fill)
{
g_return_if_fail (ST_IS_BIN (bin));
if (x_fill)
*x_fill = bin->priv->x_fill;
if (y_fill)
*y_fill = bin->priv->y_fill;
}

93
src/st/st-bin.h Normal file
View File

@ -0,0 +1,93 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-bin.h: Basic container actor
*
* Copyright 2009, 2008 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Emmanuele Bassi <ebassi@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_BIN_H__
#define __ST_BIN_H__
#include <st/st-types.h>
#include <st/st-widget.h>
G_BEGIN_DECLS
#define ST_TYPE_BIN (st_bin_get_type ())
#define ST_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BIN, StBin))
#define ST_IS_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BIN))
#define ST_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BIN, StBinClass))
#define ST_IS_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BIN))
#define ST_BIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BIN, StBinClass))
typedef struct _StBin StBin;
typedef struct _StBinPrivate StBinPrivate;
typedef struct _StBinClass StBinClass;
/**
* StBin:
*
* The #StBin struct contains only private data
*/
struct _StBin
{
/*< private >*/
StWidget parent_instance;
StBinPrivate *priv;
};
/**
* StBinClass:
*
* The #StBinClass struct contains only private data
*/
struct _StBinClass
{
/*< private >*/
StWidgetClass parent_class;
};
GType st_bin_get_type (void) G_GNUC_CONST;
StWidget * st_bin_new (void);
void st_bin_set_child (StBin *bin,
ClutterActor *child);
ClutterActor *st_bin_get_child (StBin *bin);
void st_bin_set_alignment (StBin *bin,
StAlign x_align,
StAlign y_align);
void st_bin_get_alignment (StBin *bin,
StAlign *x_align,
StAlign *y_align);
void st_bin_set_fill (StBin *bin,
gboolean x_fill,
gboolean y_fill);
void st_bin_get_fill (StBin *bin,
gboolean *x_fill,
gboolean *y_fill);
G_END_DECLS
#endif /* __ST_BIN_H__ */

92
src/st/st-border-image.c Normal file
View File

@ -0,0 +1,92 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include <config.h>
#include "st-border-image.h"
struct _StBorderImage {
GObject parent;
char *filename;
int border_top;
int border_right;
int border_bottom;
int border_left;
};
struct _StBorderImageClass {
GObjectClass parent_class;
};
G_DEFINE_TYPE (StBorderImage, st_border_image, G_TYPE_OBJECT)
static void
st_border_image_finalize (GObject *object)
{
StBorderImage *image = ST_BORDER_IMAGE (object);
g_free (image->filename);
G_OBJECT_CLASS (st_border_image_parent_class)->finalize (object);
}
static void
st_border_image_class_init (StBorderImageClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = st_border_image_finalize;
}
static void
st_border_image_init (StBorderImage *image)
{
}
StBorderImage *
st_border_image_new (const char *filename,
int border_top,
int border_right,
int border_bottom,
int border_left)
{
StBorderImage *image;
image = g_object_new (ST_TYPE_BORDER_IMAGE, NULL);
image->filename = g_strdup (filename);
image->border_top = border_top;
image->border_right = border_right;
image->border_bottom = border_bottom;
image->border_left = border_left;
return image;
}
const char *
st_border_image_get_filename (StBorderImage *image)
{
g_return_val_if_fail (ST_IS_BORDER_IMAGE (image), NULL);
return image->filename;
}
void
st_border_image_get_borders (StBorderImage *image,
int *border_top,
int *border_right,
int *border_bottom,
int *border_left)
{
g_return_if_fail (ST_IS_BORDER_IMAGE (image));
if (border_top)
*border_top = image->border_top;
if (border_right)
*border_right = image->border_right;
if (border_bottom)
*border_bottom = image->border_bottom;
if (border_left)
*border_left = image->border_left;
}

38
src/st/st-border-image.h Normal file
View File

@ -0,0 +1,38 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __ST_BORDER_IMAGE_H__
#define __ST_BORDER_IMAGE_H__
#include <glib-object.h>
G_BEGIN_DECLS
/* A StBorderImage encapsulates an image with specified unscaled borders on each edge.
*/
typedef struct _StBorderImage StBorderImage;
typedef struct _StBorderImageClass StBorderImageClass;
#define ST_TYPE_BORDER_IMAGE (st_border_image_get_type ())
#define ST_BORDER_IMAGE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_BORDER_IMAGE, StBorderImage))
#define ST_BORDER_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BORDER_IMAGE, StBorderImageClass))
#define ST_IS_BORDER_IMAGE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_BORDER_IMAGE))
#define ST_IS_BORDER_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BORDER_IMAGE))
#define ST_BORDER_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BORDER_IMAGE, StBorderImageClass))
GType st_border_image_get_type (void) G_GNUC_CONST;
StBorderImage *st_border_image_new (const char *filename,
int border_top,
int border_right,
int border_bottom,
int border_left);
const char *st_border_image_get_filename (StBorderImage *image);
void st_border_image_get_borders (StBorderImage *image,
int *border_top,
int *border_right,
int *border_bottom,
int *border_left);
G_END_DECLS
#endif /* __ST_BORDER_IMAGE_H__ */

View File

@ -0,0 +1,190 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-box-layout-child.c: box layout child actor
*
* Copyright 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Thomas Wood <thomas.wood@intel.com>
*/
/**
* SECTION:st-box-layout-child
* @short_description: meta data associated with a #StBoxLayout child.
*
* #StBoxLayoutChild is a #ClutterChildMeta implementation that stores the
* child properties for children inside a #StBoxLayout.
*/
#include "st-box-layout-child.h"
#include "st-private.h"
G_DEFINE_TYPE (StBoxLayoutChild, st_box_layout_child, CLUTTER_TYPE_CHILD_META)
#define BOX_LAYOUT_CHILD_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildPrivate))
enum
{
PROP_0,
PROP_EXPAND,
PROP_X_FILL,
PROP_Y_FILL,
PROP_X_ALIGN,
PROP_Y_ALIGN
};
static void
st_box_layout_child_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object);
switch (property_id)
{
case PROP_EXPAND:
g_value_set_boolean (value, child->expand);
break;
case PROP_X_FILL:
g_value_set_boolean (value, child->x_fill);
break;
case PROP_Y_FILL:
g_value_set_boolean (value, child->y_fill);
break;
case PROP_X_ALIGN:
g_value_set_enum (value, child->x_align);
break;
case PROP_Y_ALIGN:
g_value_set_enum (value, child->y_align);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
st_box_layout_child_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
StBoxLayoutChild *child = ST_BOX_LAYOUT_CHILD (object);
StBoxLayout *box = ST_BOX_LAYOUT (CLUTTER_CHILD_META (object)->container);
switch (property_id)
{
case PROP_EXPAND:
child->expand = g_value_get_boolean (value);
break;
case PROP_X_FILL:
child->x_fill = g_value_get_boolean (value);
break;
case PROP_Y_FILL:
child->y_fill = g_value_get_boolean (value);
break;
case PROP_X_ALIGN:
child->x_align = g_value_get_enum (value);
break;
case PROP_Y_ALIGN:
child->y_align = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
clutter_actor_queue_relayout ((ClutterActor*) box);
}
static void
st_box_layout_child_dispose (GObject *object)
{
G_OBJECT_CLASS (st_box_layout_child_parent_class)->dispose (object);
}
static void
st_box_layout_child_finalize (GObject *object)
{
G_OBJECT_CLASS (st_box_layout_child_parent_class)->finalize (object);
}
static void
st_box_layout_child_class_init (StBoxLayoutChildClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
object_class->get_property = st_box_layout_child_get_property;
object_class->set_property = st_box_layout_child_set_property;
object_class->dispose = st_box_layout_child_dispose;
object_class->finalize = st_box_layout_child_finalize;
pspec = g_param_spec_boolean ("expand", "Expand",
"Allocate the child extra space",
FALSE,
ST_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_EXPAND, pspec);
pspec = g_param_spec_boolean ("x-fill", "x-fill",
"Whether the child should receive priority "
"when the container is allocating spare space "
"on the horizontal axis",
TRUE,
ST_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_X_FILL, pspec);
pspec = g_param_spec_boolean ("y-fill", "y-fill",
"Whether the child should receive priority "
"when the container is allocating spare space "
"on the vertical axis",
TRUE,
ST_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_Y_FILL, pspec);
pspec = g_param_spec_enum ("x-align",
"X Alignment",
"X alignment of the widget within the cell",
ST_TYPE_ALIGN,
ST_ALIGN_MIDDLE,
ST_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_X_ALIGN, pspec);
pspec = g_param_spec_enum ("y-align",
"Y Alignment",
"Y alignment of the widget within the cell",
ST_TYPE_ALIGN,
ST_ALIGN_MIDDLE,
ST_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_Y_ALIGN, pspec);
}
static void
st_box_layout_child_init (StBoxLayoutChild *self)
{
self->expand = FALSE;
self->x_fill = TRUE;
self->y_fill = TRUE;
self->x_align = ST_ALIGN_MIDDLE;
self->y_align = ST_ALIGN_MIDDLE;
}

View File

@ -0,0 +1,85 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-box-layout-child.h: box layout child actor
*
* Copyright 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Thomas Wood <thomas.wood@intel.com>
*/
#ifndef _ST_BOX_LAYOUT_CHILD_H
#define _ST_BOX_LAYOUT_CHILD_H
#include <clutter/clutter.h>
#include "st-enum-types.h"
#include "st-box-layout.h"
G_BEGIN_DECLS
#define ST_TYPE_BOX_LAYOUT_CHILD st_box_layout_child_get_type()
#define ST_BOX_LAYOUT_CHILD(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChild))
#define ST_BOX_LAYOUT_CHILD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildClass))
#define ST_IS_BOX_LAYOUT_CHILD(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
ST_TYPE_BOX_LAYOUT_CHILD))
#define ST_IS_BOX_LAYOUT_CHILD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
ST_TYPE_BOX_LAYOUT_CHILD))
#define ST_BOX_LAYOUT_CHILD_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
ST_TYPE_BOX_LAYOUT_CHILD, StBoxLayoutChildClass))
typedef struct _StBoxLayoutChild StBoxLayoutChild;
typedef struct _StBoxLayoutChildClass StBoxLayoutChildClass;
typedef struct _StBoxLayoutChildPrivate StBoxLayoutChildPrivate;
/**
* StBoxLayoutChild:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
struct _StBoxLayoutChild
{
/*< private >*/
ClutterChildMeta parent;
gboolean expand;
gboolean x_fill : 1;
gboolean y_fill : 1;
StAlign x_align;
StAlign y_align;
};
struct _StBoxLayoutChildClass
{
ClutterChildMetaClass parent_class;
};
GType st_box_layout_child_get_type (void);
G_END_DECLS
#endif /* _ST_BOX_LAYOUT_CHILD_H */

1227
src/st/st-box-layout.c Normal file

File diff suppressed because it is too large Load Diff

94
src/st/st-box-layout.h Normal file
View File

@ -0,0 +1,94 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-box-layout.h: box layout actor
*
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Thomas Wood <thomas.wood@intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef _ST_BOX_LAYOUT_H
#define _ST_BOX_LAYOUT_H
#include <st/st-widget.h>
G_BEGIN_DECLS
#define ST_TYPE_BOX_LAYOUT st_box_layout_get_type()
#define ST_BOX_LAYOUT(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
ST_TYPE_BOX_LAYOUT, StBoxLayout))
#define ST_BOX_LAYOUT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
ST_TYPE_BOX_LAYOUT, StBoxLayoutClass))
#define ST_IS_BOX_LAYOUT(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
ST_TYPE_BOX_LAYOUT))
#define ST_IS_BOX_LAYOUT_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
ST_TYPE_BOX_LAYOUT))
#define ST_BOX_LAYOUT_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
ST_TYPE_BOX_LAYOUT, StBoxLayoutClass))
typedef struct _StBoxLayout StBoxLayout;
typedef struct _StBoxLayoutClass StBoxLayoutClass;
typedef struct _StBoxLayoutPrivate StBoxLayoutPrivate;
/**
* StBoxLayout:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
struct _StBoxLayout
{
/*< private >*/
StWidget parent;
StBoxLayoutPrivate *priv;
};
struct _StBoxLayoutClass
{
StWidgetClass parent_class;
};
GType st_box_layout_get_type (void);
StWidget *st_box_layout_new (void);
void st_box_layout_set_vertical (StBoxLayout *box,
gboolean vertical);
gboolean st_box_layout_get_vertical (StBoxLayout *box);
void st_box_layout_set_pack_start (StBoxLayout *box,
gboolean pack_start);
gboolean st_box_layout_get_pack_start (StBoxLayout *box);
G_END_DECLS
#endif /* _ST_BOX_LAYOUT_H */

692
src/st/st-button.c Normal file
View File

@ -0,0 +1,692 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-button.c: Plain button actor
*
* Copyright 2007 OpenedHand
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Emmanuele Bassi <ebassi@openedhand.com>
* Thomas Wood <thomas@linux.intel.com>
*
*/
/**
* SECTION:st-button
* @short_description: Button widget
*
* A button widget with support for either a text label or icon, toggle mode
* and transitions effects between states.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "st-button.h"
#include "st-marshal.h"
#include "st-texture-frame.h"
#include "st-texture-cache.h"
#include "st-private.h"
enum
{
PROP_0,
PROP_LABEL,
PROP_TOGGLE_MODE,
PROP_CHECKED,
PROP_TRANSITION_DURATION
};
enum
{
CLICKED,
LAST_SIGNAL
};
#define ST_BUTTON_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_BUTTON, StButtonPrivate))
struct _StButtonPrivate
{
gchar *text;
ClutterActor *old_bg;
gboolean old_bg_parented; /* TRUE if we have adopted old_bg */
guint8 old_opacity;
guint is_pressed : 1;
guint is_hover : 1;
guint is_checked : 1;
guint is_toggle : 1;
gint transition_duration;
ClutterAnimation *animation;
gint spacing;
};
static guint button_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE (StButton, st_button, ST_TYPE_BIN);
static void
st_button_update_label_style (StButton *button)
{
ClutterActor *label;
StThemeNode *theme_node;
ClutterColor color;
const PangoFontDescription *font;
gchar *font_string = NULL;
label = st_bin_get_child ((StBin*) button);
/* check the child is really a label */
if (!CLUTTER_IS_TEXT (label))
return;
theme_node = st_widget_get_theme_node (ST_WIDGET (button));
st_theme_node_get_foreground_color (theme_node, &color);
clutter_text_set_color (CLUTTER_TEXT (label), &color);
font = st_theme_node_get_font (theme_node);
font_string = pango_font_description_to_string (font);
clutter_text_set_font_name (CLUTTER_TEXT (label), font_string);
g_free (font_string);
}
static void
st_button_dispose_old_bg (StButton *button)
{
StButtonPrivate *priv = button->priv;
if (priv->old_bg)
{
if (priv->old_bg_parented)
{
clutter_actor_unparent (priv->old_bg);
priv->old_bg_parented = FALSE;
}
g_object_unref (priv->old_bg);
priv->old_bg = NULL;
}
}
static void
st_animation_completed (ClutterAnimation *animation,
StButton *button)
{
st_button_dispose_old_bg (button);
}
static void
st_button_style_changed (StWidget *widget)
{
StButton *button = ST_BUTTON (widget);
StButtonPrivate *priv = button->priv;
StButtonClass *button_class = ST_BUTTON_GET_CLASS (button);
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (button));
ClutterActor *bg_image;
double spacing;
st_button_dispose_old_bg (button);
bg_image = st_widget_get_border_image ((StWidget*) button);
if (bg_image)
button->priv->old_bg = g_object_ref (bg_image);
ST_WIDGET_CLASS (st_button_parent_class)->style_changed (widget);
spacing = 6;
st_theme_node_get_length (theme_node, "border-spacing", FALSE, &spacing);
priv->spacing = (int)(0.5 + spacing);
/* update the label styling */
st_button_update_label_style (button);
/* run a transition if applicable */
if (button_class->transition)
{
button_class->transition (button, priv->old_bg);
}
else
{
if (priv->old_bg &&
(!st_widget_get_style_pseudo_class (widget)))
{
ClutterAnimation *animation;
if (!clutter_actor_get_parent (priv->old_bg))
{
clutter_actor_set_parent (priv->old_bg, (ClutterActor*) widget);
priv->old_bg_parented = TRUE;
}
if (priv->transition_duration > 0)
{
animation = clutter_actor_animate (priv->old_bg,
CLUTTER_LINEAR,
priv->transition_duration,
"opacity", 0,
NULL);
g_signal_connect (animation, "completed",
G_CALLBACK (st_animation_completed), button);
}
else
{
st_button_dispose_old_bg (button);
}
}
}
}
static void
st_button_real_pressed (StButton *button)
{
st_widget_set_style_pseudo_class ((StWidget*) button, "active");
}
static void
st_button_real_released (StButton *button)
{
StButtonPrivate *priv = button->priv;
if (priv->is_checked)
st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
else if (!priv->is_hover)
st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
else
st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
}
static gboolean
st_button_button_press (ClutterActor *actor,
ClutterButtonEvent *event)
{
st_widget_hide_tooltip (ST_WIDGET (actor));
if (event->button == 1)
{
StButton *button = ST_BUTTON (actor);
StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
button->priv->is_pressed = TRUE;
clutter_grab_pointer (actor);
if (klass->pressed)
klass->pressed (button);
return TRUE;
}
return FALSE;
}
static gboolean
st_button_button_release (ClutterActor *actor,
ClutterButtonEvent *event)
{
if (event->button == 1)
{
StButton *button = ST_BUTTON (actor);
StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
if (!button->priv->is_pressed)
return FALSE;
clutter_ungrab_pointer ();
if (button->priv->is_toggle)
{
st_button_set_checked (button, !button->priv->is_checked);
}
button->priv->is_pressed = FALSE;
if (klass->released)
klass->released (button);
g_signal_emit (button, button_signals[CLICKED], 0);
return TRUE;
}
return FALSE;
}
static gboolean
st_button_enter (ClutterActor *actor,
ClutterCrossingEvent *event)
{
StButton *button = ST_BUTTON (actor);
if (!button->priv->is_checked)
st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
button->priv->is_hover = 1;
return CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event);
}
static gboolean
st_button_leave (ClutterActor *actor,
ClutterCrossingEvent *event)
{
StButton *button = ST_BUTTON (actor);
button->priv->is_hover = 0;
if (button->priv->is_pressed)
{
StButtonClass *klass = ST_BUTTON_GET_CLASS (button);
clutter_ungrab_pointer ();
button->priv->is_pressed = FALSE;
if (klass->released)
klass->released (button);
}
if (button->priv->is_checked)
st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
else
st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
return CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event);
}
static void
st_button_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StButton *button = ST_BUTTON (gobject);
StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
switch (prop_id)
{
case PROP_LABEL:
st_button_set_label (button, g_value_get_string (value));
break;
case PROP_TOGGLE_MODE:
st_button_set_toggle_mode (button, g_value_get_boolean (value));
break;
case PROP_CHECKED:
st_button_set_checked (button, g_value_get_boolean (value));
break;
case PROP_TRANSITION_DURATION:
priv->transition_duration = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_button_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
switch (prop_id)
{
case PROP_LABEL:
g_value_set_string (value, priv->text);
break;
case PROP_TOGGLE_MODE:
g_value_set_boolean (value, priv->is_toggle);
break;
case PROP_CHECKED:
g_value_set_boolean (value, priv->is_checked);
break;
case PROP_TRANSITION_DURATION:
g_value_set_int (value, priv->transition_duration);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_button_finalize (GObject *gobject)
{
StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
g_free (priv->text);
G_OBJECT_CLASS (st_button_parent_class)->finalize (gobject);
}
static void
st_button_dispose (GObject *gobject)
{
st_button_dispose_old_bg (ST_BUTTON (gobject));
G_OBJECT_CLASS (st_button_parent_class)->dispose (gobject);
}
static void
st_button_map (ClutterActor *self)
{
StButtonPrivate *priv = ST_BUTTON (self)->priv;
CLUTTER_ACTOR_CLASS (st_button_parent_class)->map (self);
if (priv->old_bg && priv->old_bg_parented)
clutter_actor_map (priv->old_bg);
}
static void
st_button_unmap (ClutterActor *self)
{
StButtonPrivate *priv = ST_BUTTON (self)->priv;
CLUTTER_ACTOR_CLASS (st_button_parent_class)->unmap (self);
if (priv->old_bg && priv->old_bg_parented)
clutter_actor_unmap (priv->old_bg);
}
static void
st_button_draw_background (StWidget *widget)
{
StButtonPrivate *priv;
ST_WIDGET_CLASS (st_button_parent_class)->draw_background (widget);
priv = ST_BUTTON (widget)->priv;
if (priv->old_bg && priv->old_bg_parented)
clutter_actor_paint (priv->old_bg);
}
static void
st_button_class_init (StButtonClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (StButtonPrivate));
klass->pressed = st_button_real_pressed;
klass->released = st_button_real_released;
gobject_class->set_property = st_button_set_property;
gobject_class->get_property = st_button_get_property;
gobject_class->dispose = st_button_dispose;
gobject_class->finalize = st_button_finalize;
actor_class->button_press_event = st_button_button_press;
actor_class->button_release_event = st_button_button_release;
actor_class->enter_event = st_button_enter;
actor_class->leave_event = st_button_leave;
actor_class->map = st_button_map;
actor_class->unmap = st_button_unmap;
widget_class->draw_background = st_button_draw_background;
widget_class->style_changed = st_button_style_changed;
pspec = g_param_spec_string ("label",
"Label",
"Label of the button",
NULL, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
pspec = g_param_spec_boolean ("toggle-mode",
"Toggle Mode",
"Enable or disable toggling",
FALSE, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TOGGLE_MODE, pspec);
pspec = g_param_spec_boolean ("checked",
"Checked",
"Indicates if a toggle button is \"on\""
" or \"off\"",
FALSE, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_CHECKED, pspec);
pspec = g_param_spec_int ("transition-duration",
"Transition Duration",
"Duration of the state transition effect",
0, G_MAXINT, 120, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TRANSITION_DURATION, pspec);
/**
* StButton::clicked:
* @button: the object that received the signal
*
* Emitted when the user activates the button, either with a mouse press and
* release or with the keyboard.
*/
button_signals[CLICKED] =
g_signal_new ("clicked",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (StButtonClass, clicked),
NULL, NULL,
_st_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
st_button_init (StButton *button)
{
button->priv = ST_BUTTON_GET_PRIVATE (button);
button->priv->transition_duration = 120;
button->priv->spacing = 6;
clutter_actor_set_reactive ((ClutterActor *) button, TRUE);
}
/**
* st_button_new:
*
* Create a new button
*
* Returns: a new #StButton
*/
StWidget *
st_button_new (void)
{
return g_object_new (ST_TYPE_BUTTON, NULL);
}
/**
* st_button_new_with_label:
* @text: text to set the label to
*
* Create a new #StButton with the specified label
*
* Returns: a new #StButton
*/
StWidget *
st_button_new_with_label (const gchar *text)
{
return g_object_new (ST_TYPE_BUTTON, "label", text, NULL);
}
/**
* st_button_get_label:
* @button: a #StButton
*
* Get the text displayed on the button
*
* Returns: the text for the button. This must not be freed by the application
*/
G_CONST_RETURN gchar *
st_button_get_label (StButton *button)
{
g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
return button->priv->text;
}
/**
* st_button_set_label:
* @button: a #Stbutton
* @text: text to set the label to
*
* Sets the text displayed on the button
*/
void
st_button_set_label (StButton *button,
const gchar *text)
{
StButtonPrivate *priv;
ClutterActor *label;
g_return_if_fail (ST_IS_BUTTON (button));
priv = button->priv;
g_free (priv->text);
if (text)
priv->text = g_strdup (text);
else
priv->text = g_strdup ("");
label = st_bin_get_child ((StBin*) button);
if (label && CLUTTER_IS_TEXT (label))
{
clutter_text_set_text (CLUTTER_TEXT (label), priv->text);
}
else
{
label = g_object_new (CLUTTER_TYPE_TEXT,
"text", priv->text,
"line-alignment", PANGO_ALIGN_CENTER,
"ellipsize", PANGO_ELLIPSIZE_END,
"use-markup", TRUE,
NULL);
st_bin_set_child ((StBin*) button, label);
}
/* Fake a style change so that we reset the style properties on the label */
st_widget_style_changed (ST_WIDGET (button));
g_object_notify (G_OBJECT (button), "label");
}
/**
* st_button_get_toggle_mode:
* @button: a #StButton
*
* Get the toggle mode status of the button.
*
* Returns: #TRUE if toggle mode is set, otherwise #FALSE
*/
gboolean
st_button_get_toggle_mode (StButton *button)
{
g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
return button->priv->is_toggle;
}
/**
* st_button_set_toggle_mode:
* @button: a #Stbutton
* @toggle: #TRUE or #FALSE
*
* Enables or disables toggle mode for the button. In toggle mode, the active
* state will be "toggled" when the user clicks the button.
*/
void
st_button_set_toggle_mode (StButton *button,
gboolean toggle)
{
g_return_if_fail (ST_IS_BUTTON (button));
button->priv->is_toggle = toggle;
g_object_notify (G_OBJECT (button), "toggle-mode");
}
/**
* st_button_get_checked:
* @button: a #StButton
*
* Get the state of the button that is in toggle mode.
*
* Returns: #TRUE if the button is checked, or #FALSE if not
*/
gboolean
st_button_get_checked (StButton *button)
{
g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
return button->priv->is_checked;
}
/**
* st_button_set_checked:
* @button: a #Stbutton
* @checked: #TRUE or #FALSE
*
* Sets the pressed state of the button. This is only really useful if the
* button has #toggle-mode mode set to #TRUE.
*/
void
st_button_set_checked (StButton *button,
gboolean checked)
{
g_return_if_fail (ST_IS_BUTTON (button));
if (button->priv->is_checked != checked)
{
button->priv->is_checked = checked;
if (checked)
st_widget_set_style_pseudo_class ((StWidget*) button, "checked");
else
if (button->priv->is_hover)
st_widget_set_style_pseudo_class ((StWidget*) button, "hover");
else
st_widget_set_style_pseudo_class ((StWidget*) button, NULL);
}
g_object_notify (G_OBJECT (button), "checked");
}

94
src/st/st-button.h Normal file
View File

@ -0,0 +1,94 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-button.h: Plain button actor
*
* Copyright 2007 OpenedHand
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Emmanuele Bassi <ebassi@openedhand.com>
* Thomas Wood <thomas@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_BUTTON_H__
#define __ST_BUTTON_H__
G_BEGIN_DECLS
#include <st/st-bin.h>
#define ST_TYPE_BUTTON (st_button_get_type ())
#define ST_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_BUTTON, StButton))
#define ST_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_BUTTON))
#define ST_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_BUTTON, StButtonClass))
#define ST_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_BUTTON))
#define ST_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_BUTTON, StButtonClass))
typedef struct _StButton StButton;
typedef struct _StButtonPrivate StButtonPrivate;
typedef struct _StButtonClass StButtonClass;
/**
* StButton:
*
* The contents of this structure is private and should only be accessed using
* the provided API.
*/
struct _StButton
{
/*< private >*/
StBin parent_instance;
StButtonPrivate *priv;
};
struct _StButtonClass
{
StBinClass parent_class;
/* vfuncs, not signals */
void (* pressed) (StButton *button);
void (* released) (StButton *button);
void (* transition) (StButton *button,
ClutterActor *old_bg);
/* signals */
void (* clicked) (StButton *button);
};
GType st_button_get_type (void) G_GNUC_CONST;
StWidget * st_button_new (void);
StWidget * st_button_new_with_label (const gchar *text);
G_CONST_RETURN gchar *st_button_get_label (StButton *button);
void st_button_set_label (StButton *button,
const gchar *text);
void st_button_set_toggle_mode (StButton *button,
gboolean toggle);
gboolean st_button_get_toggle_mode (StButton *button);
void st_button_set_checked (StButton *button,
gboolean checked);
gboolean st_button_get_checked (StButton *button);
G_END_DECLS
#endif /* __ST_BUTTON_H__ */

381
src/st/st-clipboard.c Normal file
View File

@ -0,0 +1,381 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-clipboard.c: clipboard object
*
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Thomas Wood <thomas.wood@intel.com>
*
*/
/**
* SECTION:st-clipboard
* @short_description: a simple representation of the X clipboard
*
* #StCliboard is a very simple object representation of the clipboard
* available to applications. Text is always assumed to be UTF-8 and non-text
* items are not handled.
*/
#include "st-clipboard.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <clutter/x11/clutter-x11.h>
#include <string.h>
G_DEFINE_TYPE (StClipboard, st_clipboard, G_TYPE_OBJECT)
#define CLIPBOARD_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_CLIPBOARD, StClipboardPrivate))
struct _StClipboardPrivate
{
Window clipboard_window;
gchar *clipboard_text;
Atom *supported_targets;
gint n_targets;
};
typedef struct _EventFilterData EventFilterData;
struct _EventFilterData
{
StClipboard *clipboard;
StClipboardCallbackFunc callback;
gpointer user_data;
};
static Atom __atom_clip = None;
static Atom __utf8_string = None;
static Atom __atom_targets = None;
static void
st_clipboard_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
st_clipboard_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
st_clipboard_dispose (GObject *object)
{
G_OBJECT_CLASS (st_clipboard_parent_class)->dispose (object);
}
static void
st_clipboard_finalize (GObject *object)
{
StClipboardPrivate *priv = ((StClipboard *) object)->priv;
g_free (priv->clipboard_text);
priv->clipboard_text = NULL;
g_free (priv->supported_targets);
priv->supported_targets = NULL;
priv->n_targets = 0;
G_OBJECT_CLASS (st_clipboard_parent_class)->finalize (object);
}
static ClutterX11FilterReturn
st_clipboard_provider (XEvent *xev,
ClutterEvent *cev,
StClipboard *clipboard)
{
XSelectionEvent notify_event;
XSelectionRequestEvent *req_event;
if (xev->type != SelectionRequest)
return CLUTTER_X11_FILTER_CONTINUE;
req_event = &xev->xselectionrequest;
clutter_x11_trap_x_errors ();
if (req_event->target == __atom_targets)
{
XChangeProperty (req_event->display,
req_event->requestor,
req_event->property,
XA_ATOM,
32,
PropModeReplace,
(guchar*) clipboard->priv->supported_targets,
clipboard->priv->n_targets);
}
else
{
XChangeProperty (req_event->display,
req_event->requestor,
req_event->property,
req_event->target,
8,
PropModeReplace,
(guchar*) clipboard->priv->clipboard_text,
strlen (clipboard->priv->clipboard_text));
}
notify_event.type = SelectionNotify;
notify_event.display = req_event->display;
notify_event.requestor = req_event->requestor;
notify_event.selection = req_event->selection;
notify_event.target = req_event->target;
notify_event.time = req_event->time;
if (req_event->property == None)
notify_event.property = req_event->target;
else
notify_event.property = req_event->property;
/* notify the requestor that they have a copy of the selection */
XSendEvent (req_event->display, req_event->requestor, False, 0,
(XEvent *) &notify_event);
/* Make it happen non async */
XSync (clutter_x11_get_default_display(), FALSE);
clutter_x11_untrap_x_errors (); /* FIXME: Warn here on fail ? */
return CLUTTER_X11_FILTER_REMOVE;
}
static void
st_clipboard_class_init (StClipboardClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (StClipboardPrivate));
object_class->get_property = st_clipboard_get_property;
object_class->set_property = st_clipboard_set_property;
object_class->dispose = st_clipboard_dispose;
object_class->finalize = st_clipboard_finalize;
}
static void
st_clipboard_init (StClipboard *self)
{
Display *dpy;
StClipboardPrivate *priv;
priv = self->priv = CLIPBOARD_PRIVATE (self);
priv->clipboard_window =
XCreateSimpleWindow (clutter_x11_get_default_display (),
clutter_x11_get_root_window (),
-1, -1, 1, 1, 0, 0, 0);
dpy = clutter_x11_get_default_display ();
/* Only create once */
if (__atom_clip == None)
__atom_clip = XInternAtom (dpy, "CLIPBOARD", 0);
if (__utf8_string == None)
__utf8_string = XInternAtom (dpy, "UTF8_STRING", 0);
if (__atom_targets == None)
__atom_targets = XInternAtom (dpy, "TARGETS", 0);
priv->n_targets = 2;
priv->supported_targets = g_new (Atom, priv->n_targets);
priv->supported_targets[0] = __utf8_string;
priv->supported_targets[1] = __atom_targets;
clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_provider,
self);
}
static ClutterX11FilterReturn
st_clipboard_x11_event_filter (XEvent *xev,
ClutterEvent *cev,
EventFilterData *filter_data)
{
Atom actual_type;
int actual_format, result;
unsigned long nitems, bytes_after;
unsigned char *data = NULL;
if(xev->type != SelectionNotify)
return CLUTTER_X11_FILTER_CONTINUE;
if (xev->xselection.property == None)
{
/* clipboard empty */
filter_data->callback (filter_data->clipboard,
NULL,
filter_data->user_data);
clutter_x11_remove_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
filter_data);
g_free (filter_data);
return CLUTTER_X11_FILTER_REMOVE;
}
clutter_x11_trap_x_errors ();
result = XGetWindowProperty (xev->xselection.display,
xev->xselection.requestor,
xev->xselection.property,
0L, G_MAXINT,
True,
AnyPropertyType,
&actual_type,
&actual_format,
&nitems,
&bytes_after,
&data);
if (clutter_x11_untrap_x_errors () || result != Success)
{
/* FIXME: handle failure better */
g_warning ("Clipboard: prop retrival failed");
}
filter_data->callback (filter_data->clipboard, (char*) data,
filter_data->user_data);
clutter_x11_remove_filter
((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
filter_data);
g_free (filter_data);
if (data)
XFree (data);
return CLUTTER_X11_FILTER_REMOVE;
}
/**
* st_clipboard_get_default:
*
* Get the global #StClipboard object that represents the clipboard.
*
* Returns: (transfer none): a #StClipboard owned by St and must not be
* unrefferenced or freed.
*/
StClipboard*
st_clipboard_get_default (void)
{
static StClipboard *default_clipboard = NULL;
if (!default_clipboard)
{
default_clipboard = g_object_new (ST_TYPE_CLIPBOARD, NULL);
}
return default_clipboard;
}
/**
* st_clipboard_get_text:
* @clipboard: A #StCliboard
* @callback: function to be called when the text is retreived
* @user_data: data to be passed to the callback
*
* Request the data from the clipboard in text form. @callback is executed
* when the data is retreived.
*
*/
void
st_clipboard_get_text (StClipboard *clipboard,
StClipboardCallbackFunc callback,
gpointer user_data)
{
EventFilterData *data;
Display *dpy;
g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
g_return_if_fail (callback != NULL);
data = g_new0 (EventFilterData, 1);
data->clipboard = clipboard;
data->callback = callback;
data->user_data = user_data;
clutter_x11_add_filter ((ClutterX11FilterFunc) st_clipboard_x11_event_filter,
data);
dpy = clutter_x11_get_default_display ();
clutter_x11_trap_x_errors (); /* safety on */
XConvertSelection (dpy,
__atom_clip,
__utf8_string, __utf8_string,
clipboard->priv->clipboard_window,
CurrentTime);
clutter_x11_untrap_x_errors ();
}
/**
* st_clipboard_set_text:
* @clipboard: A #StClipboard
* @text: text to copy to the clipboard
*
* Sets text as the current contents of the clipboard.
*
*/
void
st_clipboard_set_text (StClipboard *clipboard,
const gchar *text)
{
StClipboardPrivate *priv;
Display *dpy;
g_return_if_fail (ST_IS_CLIPBOARD (clipboard));
g_return_if_fail (text != NULL);
priv = clipboard->priv;
/* make a copy of the text */
g_free (priv->clipboard_text);
priv->clipboard_text = g_strdup (text);
/* tell X we own the clipboard selection */
dpy = clutter_x11_get_default_display ();
clutter_x11_trap_x_errors ();
XSetSelectionOwner (dpy, __atom_clip, priv->clipboard_window, CurrentTime);
XSync (dpy, FALSE);
clutter_x11_untrap_x_errors ();
}

103
src/st/st-clipboard.h Normal file
View File

@ -0,0 +1,103 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-clipboard.h: clipboard object
*
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Thomas Wood <thomas.wood@intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef _ST_CLIPBOARD_H
#define _ST_CLIPBOARD_H
#include <glib-object.h>
G_BEGIN_DECLS
#define ST_TYPE_CLIPBOARD st_clipboard_get_type()
#define ST_CLIPBOARD(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
ST_TYPE_CLIPBOARD, StClipboard))
#define ST_CLIPBOARD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
ST_TYPE_CLIPBOARD, StClipboardClass))
#define ST_IS_CLIPBOARD(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
ST_TYPE_CLIPBOARD))
#define ST_IS_CLIPBOARD_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
ST_TYPE_CLIPBOARD))
#define ST_CLIPBOARD_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
ST_TYPE_CLIPBOARD, StClipboardClass))
typedef struct _StClipboard StClipboard;
typedef struct _StClipboardClass StClipboardClass;
typedef struct _StClipboardPrivate StClipboardPrivate;
/**
* StClipboard:
*
* The contents of this structure is private and should only be accessed using
* the provided API.
*/
struct _StClipboard
{
/*< private >*/
GObject parent;
StClipboardPrivate *priv;
};
struct _StClipboardClass
{
GObjectClass parent_class;
};
/**
* StClipboardCallbackFunc:
* @clipboard: A #StClipboard
* @text: text from the clipboard
* @user_data: user data
*
* Callback function called when text is retrieved from the clipboard.
*/
typedef void (*StClipboardCallbackFunc) (StClipboard *clipboard,
const gchar *text,
gpointer user_data);
GType st_clipboard_get_type (void);
StClipboard* st_clipboard_get_default (void);
void st_clipboard_get_text (StClipboard *clipboard,
StClipboardCallbackFunc callback,
gpointer user_data);
void st_clipboard_set_text (StClipboard *clipboard,
const gchar *text);
G_END_DECLS
#endif /* _ST_CLIPBOARD_H */

899
src/st/st-entry.c Normal file
View File

@ -0,0 +1,899 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-entry.c: Plain entry actor
*
* Copyright 2008, 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Thomas Wood <thomas.wood@intel.com>
*
*/
/**
* SECTION:st-entry
* @short_description: Widget for displaying text
*
* #StEntry is a simple widget for displaying text. It derives from
* #StWidget to add extra style and placement functionality over
* #ClutterText. The internal #ClutterText is publicly accessibly to allow
* applications to set further properties.
*
* #StEntry supports the following pseudo style states:
* <itemizedlist>
* <listitem>
* <para>focus: the widget has focus</para>
* </listitem>
* <listitem>
* <para>indeterminate: the widget is showing the hint text</para>
* </listitem>
* </itemizedlist>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "st-entry.h"
#include "st-im-text.h"
#include "st-widget.h"
#include "st-texture-cache.h"
#include "st-marshal.h"
#include "st-clipboard.h"
#define HAS_FOCUS(actor) (clutter_actor_get_stage (actor) && clutter_stage_get_key_focus ((ClutterStage *) clutter_actor_get_stage (actor)) == actor)
/* properties */
enum
{
PROP_0,
PROP_CLUTTER_TEXT,
PROP_HINT_TEXT,
PROP_TEXT,
};
/* signals */
enum
{
PRIMARY_ICON_CLICKED,
SECONDARY_ICON_CLICKED,
LAST_SIGNAL
};
#define ST_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_ENTRY, StEntryPrivate))
#define ST_ENTRY_PRIV(x) ((StEntry *) x)->priv
struct _StEntryPrivate
{
ClutterActor *entry;
gchar *hint;
ClutterActor *primary_icon;
ClutterActor *secondary_icon;
gfloat spacing;
};
static guint entry_signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE (StEntry, st_entry, ST_TYPE_WIDGET);
static void
st_entry_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StEntry *entry = ST_ENTRY (gobject);
switch (prop_id)
{
case PROP_HINT_TEXT:
st_entry_set_hint_text (entry, g_value_get_string (value));
break;
case PROP_TEXT:
st_entry_set_text (entry, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_entry_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (gobject);
switch (prop_id)
{
case PROP_CLUTTER_TEXT:
g_value_set_object (value, priv->entry);
break;
case PROP_HINT_TEXT:
g_value_set_string (value, priv->hint);
break;
case PROP_TEXT:
g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->entry)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_entry_dispose (GObject *object)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (object);
if (priv->entry)
{
clutter_actor_unparent (priv->entry);
priv->entry = NULL;
}
}
static void
st_entry_finalize (GObject *object)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (object);
g_free (priv->hint);
priv->hint = NULL;
}
static void
st_entry_style_changed (StWidget *self)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (self);
StThemeNode *theme_node;
ClutterColor color;
const PangoFontDescription *font;
gchar *font_string;
theme_node = st_widget_get_theme_node (self);
st_theme_node_get_foreground_color (theme_node, &color);
clutter_text_set_color (CLUTTER_TEXT (priv->entry), &color);
if (st_theme_node_get_color (theme_node, "caret-color", FALSE, &color))
clutter_text_set_cursor_color (CLUTTER_TEXT (priv->entry), &color);
if (st_theme_node_get_color (theme_node, "selection-background-color", FALSE, &color))
clutter_text_set_selection_color (CLUTTER_TEXT (priv->entry), &color);
font = st_theme_node_get_font (theme_node);
font_string = pango_font_description_to_string (font);
clutter_text_set_font_name (CLUTTER_TEXT (priv->entry), font_string);
g_free (font_string);
ST_WIDGET_CLASS (st_entry_parent_class)->style_changed (self);
}
static void
st_entry_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
gfloat icon_w;
st_theme_node_adjust_for_height (theme_node, &for_height);
clutter_actor_get_preferred_width (priv->entry, for_height,
min_width_p,
natural_width_p);
if (priv->primary_icon)
{
clutter_actor_get_preferred_width (priv->primary_icon, -1, NULL, &icon_w);
if (min_width_p)
*min_width_p += icon_w + priv->spacing;
if (natural_width_p)
*natural_width_p += icon_w + priv->spacing;
}
if (priv->secondary_icon)
{
clutter_actor_get_preferred_width (priv->secondary_icon,
-1, NULL, &icon_w);
if (min_width_p)
*min_width_p += icon_w + priv->spacing;
if (natural_width_p)
*natural_width_p += icon_w + priv->spacing;
}
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
}
static void
st_entry_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
gfloat icon_h;
st_theme_node_adjust_for_width (theme_node, &for_width);
clutter_actor_get_preferred_height (priv->entry, for_width,
min_height_p,
natural_height_p);
if (priv->primary_icon)
{
clutter_actor_get_preferred_height (priv->primary_icon,
-1, NULL, &icon_h);
if (min_height_p && icon_h > *min_height_p)
*min_height_p = icon_h;
if (natural_height_p && icon_h > *natural_height_p)
*natural_height_p = icon_h;
}
if (priv->secondary_icon)
{
clutter_actor_get_preferred_height (priv->secondary_icon,
-1, NULL, &icon_h);
if (min_height_p && icon_h > *min_height_p)
*min_height_p = icon_h;
if (natural_height_p && icon_h > *natural_height_p)
*natural_height_p = icon_h;
}
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static void
st_entry_allocate (ClutterActor *actor,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
ClutterActorClass *parent_class;
ClutterActorBox content_box, child_box, icon_box;
gfloat icon_w, icon_h;
gfloat entry_h, min_h, pref_h, avail_h;
parent_class = CLUTTER_ACTOR_CLASS (st_entry_parent_class);
parent_class->allocate (actor, box, flags);
st_theme_node_get_content_box (theme_node, box, &content_box);
avail_h = content_box.y2 - content_box.y1;
child_box.x1 = content_box.x1;
child_box.x2 = content_box.x2;
if (priv->primary_icon)
{
clutter_actor_get_preferred_width (priv->primary_icon,
-1, NULL, &icon_w);
clutter_actor_get_preferred_height (priv->primary_icon,
-1, NULL, &icon_h);
icon_box.x1 = content_box.x1;
icon_box.x2 = icon_box.x1 + icon_w;
icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
icon_box.y2 = icon_box.y1 + icon_h;
clutter_actor_allocate (priv->primary_icon,
&icon_box,
flags);
/* reduce the size for the entry */
child_box.x1 += icon_w + priv->spacing;
}
if (priv->secondary_icon)
{
clutter_actor_get_preferred_width (priv->secondary_icon,
-1, NULL, &icon_w);
clutter_actor_get_preferred_height (priv->secondary_icon,
-1, NULL, &icon_h);
icon_box.x2 = content_box.x2;
icon_box.x1 = icon_box.x2 - icon_w;
icon_box.y1 = (int) (content_box.y1 + avail_h / 2 - icon_h / 2);
icon_box.y2 = icon_box.y1 + icon_h;
clutter_actor_allocate (priv->secondary_icon,
&icon_box,
flags);
/* reduce the size for the entry */
child_box.x2 -= icon_w - priv->spacing;
}
clutter_actor_get_preferred_height (priv->entry, child_box.x2 - child_box.x1,
&min_h, &pref_h);
entry_h = CLAMP (pref_h, min_h, avail_h);
child_box.y1 = (int) (content_box.y1 + avail_h / 2 - entry_h / 2);
child_box.y2 = child_box.y1 + entry_h;
clutter_actor_allocate (priv->entry, &child_box, flags);
}
static void
clutter_text_focus_in_cb (ClutterText *text,
ClutterActor *actor)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
/* remove the hint if visible */
if (priv->hint
&& !strcmp (clutter_text_get_text (text), priv->hint))
{
clutter_text_set_text (text, "");
}
st_widget_set_style_pseudo_class (ST_WIDGET (actor), "focus");
clutter_text_set_cursor_visible (text, TRUE);
}
static void
clutter_text_focus_out_cb (ClutterText *text,
ClutterActor *actor)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
/* add a hint if the entry is empty */
if (priv->hint && !strcmp (clutter_text_get_text (text), ""))
{
clutter_text_set_text (text, priv->hint);
st_widget_set_style_pseudo_class (ST_WIDGET (actor), "indeterminate");
}
else
{
st_widget_set_style_pseudo_class (ST_WIDGET (actor), NULL);
}
clutter_text_set_cursor_visible (text, FALSE);
}
static void
st_entry_paint (ClutterActor *actor)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
ClutterActorClass *parent_class;
parent_class = CLUTTER_ACTOR_CLASS (st_entry_parent_class);
parent_class->paint (actor);
clutter_actor_paint (priv->entry);
if (priv->primary_icon)
clutter_actor_paint (priv->primary_icon);
if (priv->secondary_icon)
clutter_actor_paint (priv->secondary_icon);
}
static void
st_entry_pick (ClutterActor *actor,
const ClutterColor *c)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
CLUTTER_ACTOR_CLASS (st_entry_parent_class)->pick (actor, c);
clutter_actor_paint (priv->entry);
if (priv->primary_icon)
clutter_actor_paint (priv->primary_icon);
if (priv->secondary_icon)
clutter_actor_paint (priv->secondary_icon);
}
static void
st_entry_map (ClutterActor *actor)
{
StEntryPrivate *priv = ST_ENTRY (actor)->priv;
CLUTTER_ACTOR_CLASS (st_entry_parent_class)->map (actor);
clutter_actor_map (priv->entry);
if (priv->primary_icon)
clutter_actor_map (priv->primary_icon);
if (priv->secondary_icon)
clutter_actor_map (priv->secondary_icon);
}
static void
st_entry_unmap (ClutterActor *actor)
{
StEntryPrivate *priv = ST_ENTRY (actor)->priv;
CLUTTER_ACTOR_CLASS (st_entry_parent_class)->unmap (actor);
clutter_actor_unmap (priv->entry);
if (priv->primary_icon)
clutter_actor_unmap (priv->primary_icon);
if (priv->secondary_icon)
clutter_actor_unmap (priv->secondary_icon);
}
static void
st_entry_clipboard_callback (StClipboard *clipboard,
const gchar *text,
gpointer data)
{
ClutterText *ctext = (ClutterText*)((StEntry *) data)->priv->entry;
gint cursor_pos;
if (!text)
return;
/* delete the current selection before pasting */
clutter_text_delete_selection (ctext);
/* "paste" the clipboard text into the entry */
cursor_pos = clutter_text_get_cursor_position (ctext);
clutter_text_insert_text (ctext, text, cursor_pos);
}
static gboolean
st_entry_key_press_event (ClutterActor *actor,
ClutterKeyEvent *event)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
/* This is expected to handle events that were emitted for the inner
ClutterText. They only reach this function if the ClutterText
didn't handle them */
/* paste */
if ((event->modifier_state & CLUTTER_CONTROL_MASK)
&& event->keyval == CLUTTER_v)
{
StClipboard *clipboard;
clipboard = st_clipboard_get_default ();
st_clipboard_get_text (clipboard, st_entry_clipboard_callback, actor);
return TRUE;
}
/* copy */
if ((event->modifier_state & CLUTTER_CONTROL_MASK)
&& event->keyval == CLUTTER_c)
{
StClipboard *clipboard;
gchar *text;
clipboard = st_clipboard_get_default ();
text = clutter_text_get_selection ((ClutterText*) priv->entry);
if (text && strlen (text))
st_clipboard_set_text (clipboard, text);
return TRUE;
}
/* cut */
if ((event->modifier_state & CLUTTER_CONTROL_MASK)
&& event->keyval == CLUTTER_x)
{
StClipboard *clipboard;
gchar *text;
clipboard = st_clipboard_get_default ();
text = clutter_text_get_selection ((ClutterText*) priv->entry);
if (text && strlen (text))
{
st_clipboard_set_text (clipboard, text);
/* now delete the text */
clutter_text_delete_selection ((ClutterText *) priv->entry);
}
return TRUE;
}
return FALSE;
}
static void
st_entry_key_focus_in (ClutterActor *actor)
{
StEntryPrivate *priv = ST_ENTRY_PRIV (actor);
/* We never want key focus. The ClutterText should be given first
pass for all key events */
clutter_actor_grab_key_focus (priv->entry);
}
static void
st_entry_class_init (StEntryClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (StEntryPrivate));
gobject_class->set_property = st_entry_set_property;
gobject_class->get_property = st_entry_get_property;
gobject_class->finalize = st_entry_finalize;
gobject_class->dispose = st_entry_dispose;
actor_class->get_preferred_width = st_entry_get_preferred_width;
actor_class->get_preferred_height = st_entry_get_preferred_height;
actor_class->allocate = st_entry_allocate;
actor_class->paint = st_entry_paint;
actor_class->pick = st_entry_pick;
actor_class->map = st_entry_map;
actor_class->unmap = st_entry_unmap;
actor_class->key_press_event = st_entry_key_press_event;
actor_class->key_focus_in = st_entry_key_focus_in;
widget_class->style_changed = st_entry_style_changed;
pspec = g_param_spec_object ("clutter-text",
"Clutter Text",
"Internal ClutterText actor",
CLUTTER_TYPE_TEXT,
G_PARAM_READABLE);
g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec);
pspec = g_param_spec_string ("hint-text",
"Hint Text",
"Text to display when the entry is not focused "
"and the text property is empty",
NULL, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_HINT_TEXT, pspec);
pspec = g_param_spec_string ("text",
"Text",
"Text of the entry",
NULL, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
/* signals */
/**
* StEntry::primary-icon-clicked:
*
* Emitted when the primary icon is clicked
*/
entry_signals[PRIMARY_ICON_CLICKED] =
g_signal_new ("primary-icon-clicked",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (StEntryClass, primary_icon_clicked),
NULL, NULL,
_st_marshal_VOID__VOID,
G_TYPE_NONE, 0);
/**
* StEntry::secondary-icon-clicked:
*
* Emitted when the secondary icon is clicked
*/
entry_signals[SECONDARY_ICON_CLICKED] =
g_signal_new ("secondary-icon-clicked",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (StEntryClass, secondary_icon_clicked),
NULL, NULL,
_st_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
st_entry_init (StEntry *entry)
{
StEntryPrivate *priv;
priv = entry->priv = ST_ENTRY_GET_PRIVATE (entry);
priv->entry = g_object_new (ST_TYPE_IM_TEXT,
"line-alignment", PANGO_ALIGN_LEFT,
"editable", TRUE,
"reactive", TRUE,
"single-line-mode", TRUE,
NULL);
g_signal_connect (priv->entry, "key-focus-in",
G_CALLBACK (clutter_text_focus_in_cb), entry);
g_signal_connect (priv->entry, "key-focus-out",
G_CALLBACK (clutter_text_focus_out_cb), entry);
priv->spacing = 6.0f;
clutter_actor_set_parent (priv->entry, CLUTTER_ACTOR (entry));
clutter_actor_set_reactive ((ClutterActor *) entry, TRUE);
/* set cursor hidden until we receive focus */
clutter_text_set_cursor_visible ((ClutterText *) priv->entry, FALSE);
}
/**
* st_entry_new:
* @text: text to set the entry to
*
* Create a new #StEntry with the specified entry
*
* Returns: a new #StEntry
*/
StWidget *
st_entry_new (const gchar *text)
{
StWidget *entry;
/* add the entry to the stage, but don't allow it to be visible */
entry = g_object_new (ST_TYPE_ENTRY,
"text", text,
NULL);
return entry;
}
/**
* st_entry_get_text:
* @entry: a #StEntry
*
* Get the text displayed on the entry
*
* Returns: the text for the entry. This must not be freed by the application
*/
G_CONST_RETURN gchar *
st_entry_get_text (StEntry *entry)
{
g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
return clutter_text_get_text (CLUTTER_TEXT (entry->priv->entry));
}
/**
* st_entry_set_text:
* @entry: a #StEntry
* @text: text to set the entry to
*
* Sets the text displayed on the entry
*/
void
st_entry_set_text (StEntry *entry,
const gchar *text)
{
StEntryPrivate *priv;
g_return_if_fail (ST_IS_ENTRY (entry));
priv = entry->priv;
/* set a hint if we are blanking the entry */
if (priv->hint
&& text && !strcmp ("", text)
&& !HAS_FOCUS (priv->entry))
{
text = priv->hint;
st_widget_set_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
}
else
{
if (HAS_FOCUS (priv->entry))
st_widget_set_style_pseudo_class (ST_WIDGET (entry), "focus");
else
st_widget_set_style_pseudo_class (ST_WIDGET (entry), NULL);
}
clutter_text_set_text (CLUTTER_TEXT (priv->entry), text);
g_object_notify (G_OBJECT (entry), "text");
}
/**
* st_entry_get_clutter_text:
* @entry: a #StEntry
*
* Retrieve the internal #ClutterText so that extra parameters can be set
*
* Returns: (transfer none): the #ClutterText used by #StEntry. The entry is
* owned by the #StEntry and should not be unref'ed by the application.
*/
ClutterActor*
st_entry_get_clutter_text (StEntry *entry)
{
g_return_val_if_fail (ST_ENTRY (entry), NULL);
return entry->priv->entry;
}
/**
* st_entry_set_hint_text:
* @entry: a #StEntry
* @text: text to set as the entry hint
*
* Sets the text to display when the entry is empty and unfocused. When the
* entry is displaying the hint, it has a pseudo class of "indeterminate".
* A value of NULL unsets the hint.
*/
void
st_entry_set_hint_text (StEntry *entry,
const gchar *text)
{
StEntryPrivate *priv;
g_return_if_fail (ST_IS_ENTRY (entry));
priv = entry->priv;
g_free (priv->hint);
priv->hint = g_strdup (text);
if (!strcmp (clutter_text_get_text (CLUTTER_TEXT (priv->entry)), ""))
{
clutter_text_set_text (CLUTTER_TEXT (priv->entry), priv->hint);
st_widget_set_style_pseudo_class (ST_WIDGET (entry), "indeterminate");
}
}
/**
* st_entry_get_hint_text:
* @entry: a #StEntry
*
* Gets the text that is displayed when the entry is empty and unfocused
*
* Returns: the current value of the hint property. This string is owned by the
* #StEntry and should not be freed or modified.
*/
G_CONST_RETURN
gchar *
st_entry_get_hint_text (StEntry *entry)
{
g_return_val_if_fail (ST_IS_ENTRY (entry), NULL);
return entry->priv->hint;
}
static gboolean
_st_entry_icon_press_cb (ClutterActor *actor,
ClutterButtonEvent *event,
StEntry *entry)
{
StEntryPrivate *priv = entry->priv;
if (actor == priv->primary_icon)
g_signal_emit (entry, entry_signals[PRIMARY_ICON_CLICKED], 0);
else
g_signal_emit (entry, entry_signals[SECONDARY_ICON_CLICKED], 0);
return FALSE;
}
static void
_st_entry_set_icon_from_file (StEntry *entry,
ClutterActor **icon,
const gchar *filename)
{
if (*icon)
{
g_signal_handlers_disconnect_by_func (*icon,
_st_entry_icon_press_cb,
entry);
clutter_actor_unparent (*icon);
*icon = NULL;
}
if (filename)
{
StTextureCache *cache;
cache = st_texture_cache_get_default ();
*icon = (ClutterActor*) st_texture_cache_get_texture (cache, filename);
clutter_actor_set_reactive (*icon, TRUE);
clutter_actor_set_parent (*icon, CLUTTER_ACTOR (entry));
g_signal_connect (*icon, "button-release-event",
G_CALLBACK (_st_entry_icon_press_cb), entry);
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (entry));
}
/**
* st_entry_set_primary_icon_from_file:
* @entry: a #StEntry
* @filename: filename of an icon
*
* Set the primary icon of the entry to the given filename
*/
void
st_entry_set_primary_icon_from_file (StEntry *entry,
const gchar *filename)
{
StEntryPrivate *priv;
g_return_if_fail (ST_IS_ENTRY (entry));
priv = entry->priv;
_st_entry_set_icon_from_file (entry, &priv->primary_icon, filename);
}
/**
* st_entry_set_secondary_icon_from_file:
* @entry: a #StEntry
* @filename: filename of an icon
*
* Set the primary icon of the entry to the given filename
*/
void
st_entry_set_secondary_icon_from_file (StEntry *entry,
const gchar *filename)
{
StEntryPrivate *priv;
g_return_if_fail (ST_IS_ENTRY (entry));
priv = entry->priv;
_st_entry_set_icon_from_file (entry, &priv->secondary_icon, filename);
}

89
src/st/st-entry.h Normal file
View File

@ -0,0 +1,89 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-entry.h: Plain entry actor
*
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_ENTRY_H__
#define __ST_ENTRY_H__
G_BEGIN_DECLS
#include <st/st-widget.h>
#define ST_TYPE_ENTRY (st_entry_get_type ())
#define ST_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_ENTRY, StEntry))
#define ST_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_ENTRY))
#define ST_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_ENTRY, StEntryClass))
#define ST_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_ENTRY))
#define ST_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_ENTRY, StEntryClass))
typedef struct _StEntry StEntry;
typedef struct _StEntryPrivate StEntryPrivate;
typedef struct _StEntryClass StEntryClass;
/**
* StEntry:
*
* The contents of this structure is private and should only be accessed using
* the provided API.
*/
struct _StEntry
{
/*< private >*/
StWidget parent_instance;
StEntryPrivate *priv;
};
struct _StEntryClass
{
StWidgetClass parent_class;
/* signals */
void (*primary_icon_clicked) (StEntry *entry);
void (*secondary_icon_clicked) (StEntry *entry);
};
GType st_entry_get_type (void) G_GNUC_CONST;
StWidget * st_entry_new (const gchar *text);
G_CONST_RETURN gchar *st_entry_get_text (StEntry *entry);
void st_entry_set_text (StEntry *entry,
const gchar *text);
ClutterActor* st_entry_get_clutter_text (StEntry *entry);
void st_entry_set_hint_text (StEntry *entry,
const gchar *text);
G_CONST_RETURN gchar *st_entry_get_hint_text (StEntry *entry);
void st_entry_set_primary_icon_from_file (StEntry *entry,
const gchar *filename);
void st_entry_set_secondary_icon_from_file (StEntry *entry,
const gchar *filename);
G_END_DECLS
#endif /* __ST_ENTRY_H__ */

30
src/st/st-enum-types.c.in Normal file
View File

@ -0,0 +1,30 @@
/*** BEGIN file-header ***/
#include "st-enum-types.h"
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
#include "@filename@"
/*** END file-production ***/
/*** BEGIN value-header ***/
GType
@enum_name@_get_type(void) {
static GType enum_type_id = 0;
if (G_UNLIKELY (!enum_type_id))
{
static const G@Type@Value values[] = {
/*** END value-header ***/
/*** BEGIN value-production ***/
{ @VALUENAME@, "@VALUENAME@", "@valuenick@" },
/*** END value-production ***/
/*** BEGIN value-tail ***/
{ 0, NULL, NULL }
};
enum_type_id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
}
return enum_type_id;
}
/*** END value-tail ***/

29
src/st/st-enum-types.h.in Normal file
View File

@ -0,0 +1,29 @@
/*** BEGIN file-header ***/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_ENUM_TYPES_H__
#define __ST_ENUM_TYPES_H__
#include <glib-object.h>
G_BEGIN_DECLS
/*** END file-header ***/
/*** BEGIN file-production ***/
/* enumerations from "@filename@" */
/*** END file-production ***/
/*** BEGIN file-tail ***/
G_END_DECLS
#endif /* !__ST_ENUM_TYPES_H__ */
/*** END file-tail ***/
/*** BEGIN value-header ***/
GType @enum_name@_get_type (void) G_GNUC_CONST;
#define ST_TYPE_@ENUMSHORT@ (@enum_name@_get_type())
/*** END value-header ***/

477
src/st/st-im-text.c Normal file
View File

@ -0,0 +1,477 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-im-text.c
*
* This started as a copy of ClutterIMText converted to use
* GtkIMContext rather than ClutterIMContext. Original code:
*
* Author: raymond liu <raymond.liu@intel.com>
*
* Copyright (C) 2009, Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
/**
* SECTION:StIMText
* @short_description: Text widget with input method support
* @stability: Unstable
* @see_also: #ClutterText
* @include: st-imtext/st-imtext.h
*
* #StIMText derives from ClutterText and hooks up better text input
* via #GtkIMContext. It is meant to be a drop-in replacement for
* ClutterIMText but using GtkIMContext rather than ClutterIMContext.
*/
/* Places where this actor doesn't support all of GtkIMContext:
*
* A) It doesn't support preedit. This makes it fairly useless for
* most complicated input methods. Fixing this requires support
* directly in ClutterText, since there is no way to wedge a
* preedit string in externally.
* B) It doesn't support surrounding context via the
* :retrieve-surrounding and :delete-surrounding signals. This could
* be added here, but only affects a small number of input methods
* and really doesn't make a lot of sense without A)
*
* Another problem that will show up with usage in GNOME Shell's overview
* is that the user may have trouble seeing and interacting with ancilliary
* windows shown by the IM.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <clutter/clutter.h>
#include <clutter/x11/clutter-x11.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <X11/extensions/XKB.h>
#include "st-im-text.h"
#define ST_IM_TEXT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_IM_TEXT, StIMTextPrivate))
struct _StIMTextPrivate
{
GtkIMContext *im_context;
GdkWindow *window;
guint need_im_reset : 1;
};
static void st_im_text_commit_cb (GtkIMContext *context,
const gchar *str,
StIMText *imtext);
G_DEFINE_TYPE (StIMText, st_im_text, CLUTTER_TYPE_TEXT)
static void
st_im_text_dispose (GObject *object)
{
StIMTextPrivate *priv = ST_IM_TEXT (object)->priv;
g_signal_handlers_disconnect_by_func (priv->im_context,
(void *) st_im_text_commit_cb,
object);
g_object_unref (priv->im_context);
priv->im_context = NULL;
}
static void
update_im_cursor_location (StIMText *self)
{
StIMTextPrivate *priv = self->priv;
ClutterText *clutter_text = CLUTTER_TEXT (self);
ClutterActor *parent;
gint position;
gfloat cursor_x, cursor_y, cursor_height;
gfloat actor_x, actor_y;
GdkRectangle area;
position = clutter_text_get_cursor_position (clutter_text);
clutter_text_position_to_coords (clutter_text, position,
&cursor_x, &cursor_y, &cursor_height);
/* This is a workaround for a bug in Clutter where
* clutter_actor_get_transformed_position doesn't work during
* clutter_actor_paint() because the actor has already set up
* a model-view matrix.
*
* http://bugzilla.openedhand.com/show_bug.cgi?id=1115
*/
actor_x = actor_y = 0.;
parent = CLUTTER_ACTOR (self);
while (parent)
{
gfloat x, y;
clutter_actor_get_position (parent, &x, &y);
actor_x += x;
actor_y += y;
parent = clutter_actor_get_parent (parent);
}
area.x = (int)(0.5 + cursor_x + actor_x);
area.y = (int)(0.5 + cursor_y + actor_y);
area.width = 0;
area.height = (int)(0.5 + cursor_height);
gtk_im_context_set_cursor_location (priv->im_context, &area);
}
static void
st_im_text_commit_cb (GtkIMContext *context,
const gchar *str,
StIMText *imtext)
{
ClutterText *clutter_text = CLUTTER_TEXT (imtext);
if (clutter_text_get_editable (clutter_text))
{
clutter_text_delete_selection (clutter_text);
clutter_text_insert_text (clutter_text, str,
clutter_text_get_cursor_position (clutter_text));
}
}
static void
reset_im_context (StIMText *self)
{
StIMTextPrivate *priv = self->priv;
if (priv->need_im_reset)
{
gtk_im_context_reset (priv->im_context);
priv->need_im_reset = FALSE;
}
}
static void
st_im_text_paint (ClutterActor *actor)
{
StIMText *self = ST_IM_TEXT (actor);
ClutterText *clutter_text = CLUTTER_TEXT (actor);
/* This updates the cursor position as a side-effect */
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->paint)
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->paint (actor);
if (clutter_text_get_editable (clutter_text))
update_im_cursor_location (self);
}
/* Returns a new reference to window */
static GdkWindow *
window_for_actor (ClutterActor *actor)
{
GdkDisplay *display = gdk_display_get_default ();
ClutterActor *stage;
Window xwindow;
GdkWindow *window;
stage = clutter_actor_get_stage (actor);
xwindow = clutter_x11_get_stage_window ((ClutterStage *)stage);
window = gdk_window_lookup_for_display (display, xwindow);
if (window)
g_object_ref (window);
else
window = gdk_window_foreign_new_for_display (display, xwindow);
return window;
}
static void
st_im_text_realize (ClutterActor *actor)
{
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
priv->window = window_for_actor (actor);
gtk_im_context_set_client_window (priv->im_context, priv->window);
}
static void
st_im_text_unrealize (ClutterActor *actor)
{
StIMText *self = ST_IM_TEXT (actor);
StIMTextPrivate *priv = self->priv;
reset_im_context (self);
gtk_im_context_set_client_window (priv->im_context, NULL);
g_object_unref (priv->window);
priv->window = NULL;
}
static gboolean
key_is_modifier (guint16 keyval)
{
/* See gdkkeys-x11.c:_gdk_keymap_key_is_modifier() for how this
* really should be implemented */
switch (keyval)
{
case GDK_Shift_L:
case GDK_Shift_R:
case GDK_Control_L:
case GDK_Control_R:
case GDK_Caps_Lock:
case GDK_Shift_Lock:
case GDK_Meta_L:
case GDK_Meta_R:
case GDK_Alt_L:
case GDK_Alt_R:
case GDK_Super_L:
case GDK_Super_R:
case GDK_Hyper_L:
case GDK_Hyper_R:
case GDK_ISO_Lock:
case GDK_ISO_Level2_Latch:
case GDK_ISO_Level3_Shift:
case GDK_ISO_Level3_Latch:
case GDK_ISO_Level3_Lock:
case GDK_ISO_Level5_Shift:
case GDK_ISO_Level5_Latch:
case GDK_ISO_Level5_Lock:
case GDK_ISO_Group_Shift:
case GDK_ISO_Group_Latch:
case GDK_ISO_Group_Lock:
return TRUE;
default:
return FALSE;
}
}
static GdkEventKey *
key_event_to_gdk (ClutterKeyEvent *event_clutter)
{
GdkDisplay *display = gdk_display_get_default ();
GdkKeymap *keymap = gdk_keymap_get_for_display (display);
GdkEventKey *event_gdk;
event_gdk = (GdkEventKey *)gdk_event_new ((event_clutter->type == CLUTTER_KEY_PRESS) ?
GDK_KEY_PRESS : GDK_KEY_RELEASE);
event_gdk->window = window_for_actor ((ClutterActor *)event_clutter->stage);
event_gdk->send_event = FALSE;
event_gdk->time = event_clutter->time;
/* This depends on ClutterModifierType and GdkModifierType being
* identical, which they are currently. (They both match the X
* modifier state in the low 16-bits and have the same extensions.) */
event_gdk->state = event_clutter->modifier_state;
event_gdk->keyval = event_clutter->keyval;
event_gdk->hardware_keycode = event_clutter->hardware_keycode;
/* For non-proper non-XKB support, we'd need a huge cut-and-paste
* from gdkkeys-x11.c; this is a macro that just shifts a few bits
* out of state, so won't make the situation worse if the server
* doesn't support XKB; we'll just end up with group == 0 */
event_gdk->group = XkbGroupForCoreState (event_gdk->state);
gdk_keymap_translate_keyboard_state (keymap, event_gdk->hardware_keycode,
event_gdk->state, event_gdk->group,
&event_gdk->keyval, NULL, NULL, NULL);
if (event_clutter->unicode_value)
{
/* This is not particularly close to what GDK does - event_gdk->string
* is supposed to be in the locale encoding, and have control keys
* as control characters, etc. See gdkevents-x11.c:translate_key_event().
* Hopefully no input method is using event.string.
*/
char buf[6];
event_gdk->length = g_unichar_to_utf8 (event_clutter->unicode_value, buf);
event_gdk->string = g_strndup (buf, event_gdk->length);
}
event_gdk->is_modifier = key_is_modifier (event_gdk->keyval);
return event_gdk;
}
static gboolean
st_im_text_button_press_event (ClutterActor *actor,
ClutterButtonEvent *event)
{
/* The button press indicates the user moving the cursor, or selecting
* etc, so we should abort any current preedit operation. ClutterText
* treats all buttons identically, so so do we.
*/
reset_im_context (ST_IM_TEXT (actor));
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->button_press_event)
return CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->button_press_event (actor, event);
else
return FALSE;
}
static gboolean
st_im_text_key_press_event (ClutterActor *actor,
ClutterKeyEvent *event)
{
StIMText *self = ST_IM_TEXT (actor);
StIMTextPrivate *priv = self->priv;
ClutterText *clutter_text = CLUTTER_TEXT (actor);
gboolean result = FALSE;
int old_position;
if (clutter_text_get_editable (clutter_text))
{
GdkEventKey *event_gdk = key_event_to_gdk (event);
if (gtk_im_context_filter_keypress (priv->im_context, event_gdk))
{
priv->need_im_reset = TRUE;
result = TRUE;
}
gdk_event_free ((GdkEvent *)event_gdk);
}
/* ClutterText:position isn't properly notified, so we have to
* check before/after to catch a keypress (like an arrow key)
* moving the cursor position, which should reset the IM context.
* (Resetting on notify::position would require a sentinel when
* committing text)
*
* http://bugzilla.openedhand.com/show_bug.cgi?id=1830
*/
old_position = clutter_text_get_cursor_position (clutter_text);
if (!result &&
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_press_event)
result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_press_event (actor, event);
if (clutter_text_get_cursor_position (clutter_text) != old_position)
reset_im_context (self);
return result;
}
static gboolean
st_im_text_key_release_event (ClutterActor *actor,
ClutterKeyEvent *event)
{
StIMText *self = ST_IM_TEXT (actor);
StIMTextPrivate *priv = self->priv;
ClutterText *clutter_text = CLUTTER_TEXT (actor);
GdkEventKey *event_gdk;
gboolean result = FALSE;
if (clutter_text_get_editable (clutter_text))
{
event_gdk = key_event_to_gdk (event);
if (gtk_im_context_filter_keypress (priv->im_context, event_gdk))
{
priv->need_im_reset = TRUE;
result = TRUE;
}
gdk_event_free ((GdkEvent *)event_gdk);
}
if (!result &&
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event)
result = CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_release_event (actor, event);
return result;
}
static void
st_im_text_key_focus_in (ClutterActor *actor)
{
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
ClutterText *clutter_text = CLUTTER_TEXT (actor);
if (clutter_text_get_editable (clutter_text))
{
priv->need_im_reset = TRUE;
gtk_im_context_focus_in (priv->im_context);
}
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_in)
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_in (actor);
}
static void
st_im_text_key_focus_out (ClutterActor *actor)
{
StIMTextPrivate *priv = ST_IM_TEXT (actor)->priv;
ClutterText *clutter_text = CLUTTER_TEXT (actor);
if (clutter_text_get_editable (clutter_text))
{
priv->need_im_reset = TRUE;
gtk_im_context_focus_out (priv->im_context);
}
if (CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_out)
CLUTTER_ACTOR_CLASS (st_im_text_parent_class)->key_focus_out (actor);
}
static void
st_im_text_class_init (StIMTextClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
g_type_class_add_private (klass, sizeof (StIMTextPrivate));
object_class->dispose = st_im_text_dispose;
actor_class->paint = st_im_text_paint;
actor_class->realize = st_im_text_realize;
actor_class->unrealize = st_im_text_unrealize;
actor_class->button_press_event = st_im_text_button_press_event;
actor_class->key_press_event = st_im_text_key_press_event;
actor_class->key_release_event = st_im_text_key_release_event;
actor_class->key_focus_in = st_im_text_key_focus_in;
actor_class->key_focus_out = st_im_text_key_focus_out;
}
static void
st_im_text_init (StIMText *self)
{
StIMTextPrivate *priv;
self->priv = priv = ST_IM_TEXT_GET_PRIVATE (self);
priv->im_context = gtk_im_multicontext_new ();
g_signal_connect (priv->im_context, "commit",
G_CALLBACK (st_im_text_commit_cb), self);
}
/**
* st_im_text_new:
* @text: text to set to
*
* Create a new #StIMText with the specified text
*
* Returns: a new #ClutterActor
*/
ClutterActor *
st_im_text_new (const gchar *text)
{
return g_object_new (ST_TYPE_IM_TEXT,
"text", text,
NULL);
}

69
src/st/st-im-text.h Normal file
View File

@ -0,0 +1,69 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-imtext.h
*
* This is a copy of ClutterIMText converted to use GtkIMContext rather
* than ClutterIMContext. Original code:
*
* Author: raymond liu <raymond.liu@intel.com>
*
* Copyright (C) 2009, Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_IM_TEXT_H__
#define __ST_IM_TEXT_H__
G_BEGIN_DECLS
#include <clutter/clutter.h>
#define ST_TYPE_IM_TEXT (st_im_text_get_type ())
#define ST_IM_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_IM_TEXT, StIMText))
#define ST_IS_IM_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_IM_TEXT))
#define ST_IM_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_IM_TEXT, StIMTextClass))
#define ST_IS_IM_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_IM_TEXT))
#define ST_IM_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_IM_TEXT, StIMTextClass))
typedef struct _StIMText StIMText;
typedef struct _StIMTextPrivate StIMTextPrivate;
typedef struct _StIMTextClass StIMTextClass;
struct _StIMText
{
ClutterText parent_instance;
StIMTextPrivate *priv;
};
struct _StIMTextClass
{
ClutterTextClass parent_class;
};
GType st_im_text_get_type (void) G_GNUC_CONST;
ClutterActor *st_im_text_new (const gchar *text);
void st_im_text_set_autoshow_im (StIMText *self,
gboolean autoshow);
G_END_DECLS
#endif /* __ST_IM_TEXT_H__ */

342
src/st/st-label.c Normal file
View File

@ -0,0 +1,342 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-label.c: Plain label actor
*
* Copyright 2008,2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
/**
* SECTION:st-label
* @short_description: Widget for displaying text
*
* #StLabel is a simple widget for displaying text. It derives from
* #StWidget to add extra style and placement functionality over
* #ClutterText. The internal #ClutterText is publicly accessibly to allow
* applications to set further properties.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "st-label.h"
#include "st-widget.h"
enum
{
PROP_0,
PROP_CLUTTER_TEXT,
PROP_TEXT
};
#define ST_LABEL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_LABEL, StLabelPrivate))
struct _StLabelPrivate
{
ClutterActor *label;
};
G_DEFINE_TYPE (StLabel, st_label, ST_TYPE_WIDGET);
static void
st_label_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StLabel *label = ST_LABEL (gobject);
switch (prop_id)
{
case PROP_TEXT:
st_label_set_text (label, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_label_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StLabelPrivate *priv = ST_LABEL (gobject)->priv;
switch (prop_id)
{
case PROP_CLUTTER_TEXT:
g_value_set_object (value, priv->label);
break;
case PROP_TEXT:
g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_label_style_changed (StWidget *self)
{
StLabelPrivate *priv;
StThemeNode *theme_node;
ClutterColor color;
const PangoFontDescription *font;
gchar *font_string;
priv = ST_LABEL (self)->priv;
theme_node = st_widget_get_theme_node (self);
st_theme_node_get_foreground_color (theme_node, &color);
clutter_text_set_color (CLUTTER_TEXT (priv->label), &color);
font = st_theme_node_get_font (theme_node);
font_string = pango_font_description_to_string (font);
clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string);
g_free (font_string);
ST_WIDGET_CLASS (st_label_parent_class)->style_changed (self);
}
static void
st_label_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
st_theme_node_adjust_for_height (theme_node, &for_height);
clutter_actor_get_preferred_width (priv->label, for_height,
min_width_p,
natural_width_p);
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
}
static void
st_label_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
st_theme_node_adjust_for_width (theme_node, &for_width);
clutter_actor_get_preferred_height (priv->label, for_width,
min_height_p,
natural_height_p);
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static void
st_label_allocate (ClutterActor *actor,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
ClutterActorClass *parent_class;
ClutterActorBox content_box;
st_theme_node_get_content_box (theme_node, box, &content_box);
parent_class = CLUTTER_ACTOR_CLASS (st_label_parent_class);
parent_class->allocate (actor, box, flags);
clutter_actor_allocate (priv->label, &content_box, flags);
}
static void
st_label_paint (ClutterActor *actor)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
ClutterActorClass *parent_class;
parent_class = CLUTTER_ACTOR_CLASS (st_label_parent_class);
parent_class->paint (actor);
clutter_actor_paint (priv->label);
}
static void
st_label_map (ClutterActor *actor)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
CLUTTER_ACTOR_CLASS (st_label_parent_class)->map (actor);
clutter_actor_map (priv->label);
}
static void
st_label_unmap (ClutterActor *actor)
{
StLabelPrivate *priv = ST_LABEL (actor)->priv;
CLUTTER_ACTOR_CLASS (st_label_parent_class)->unmap (actor);
clutter_actor_unmap (priv->label);
}
static void
st_label_class_init (StLabelClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (StLabelPrivate));
gobject_class->set_property = st_label_set_property;
gobject_class->get_property = st_label_get_property;
actor_class->paint = st_label_paint;
actor_class->allocate = st_label_allocate;
actor_class->get_preferred_width = st_label_get_preferred_width;
actor_class->get_preferred_height = st_label_get_preferred_height;
actor_class->map = st_label_map;
actor_class->unmap = st_label_unmap;
widget_class->style_changed = st_label_style_changed;
pspec = g_param_spec_object ("clutter-text",
"Clutter Text",
"Internal ClutterText actor",
CLUTTER_TYPE_TEXT,
G_PARAM_READABLE);
g_object_class_install_property (gobject_class, PROP_CLUTTER_TEXT, pspec);
pspec = g_param_spec_string ("text",
"Text",
"Text of the label",
NULL, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TEXT, pspec);
}
static void
st_label_init (StLabel *label)
{
StLabelPrivate *priv;
label->priv = priv = ST_LABEL_GET_PRIVATE (label);
label->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
"ellipsize", PANGO_ELLIPSIZE_END,
NULL);
clutter_actor_set_parent (priv->label, CLUTTER_ACTOR (label));
}
/**
* st_label_new:
* @text: text to set the label to
*
* Create a new #StLabel with the specified label
*
* Returns: a new #StLabel
*/
StWidget *
st_label_new (const gchar *text)
{
if (text == NULL || *text == '\0')
return g_object_new (ST_TYPE_LABEL, NULL);
else
return g_object_new (ST_TYPE_LABEL,
"text", text,
NULL);
}
/**
* st_label_get_text:
* @label: a #StLabel
*
* Get the text displayed on the label
*
* Returns: the text for the label. This must not be freed by the application
*/
G_CONST_RETURN gchar *
st_label_get_text (StLabel *label)
{
g_return_val_if_fail (ST_IS_LABEL (label), NULL);
return clutter_text_get_text (CLUTTER_TEXT (label->priv->label));
}
/**
* st_label_set_text:
* @label: a #StLabel
* @text: text to set the label to
*
* Sets the text displayed on the label
*/
void
st_label_set_text (StLabel *label,
const gchar *text)
{
StLabelPrivate *priv;
g_return_if_fail (ST_IS_LABEL (label));
g_return_if_fail (text != NULL);
priv = label->priv;
clutter_text_set_text (CLUTTER_TEXT (priv->label), text);
g_object_notify (G_OBJECT (label), "text");
}
/**
* st_label_get_clutter_text:
* @label: a #StLabel
*
* Retrieve the internal #ClutterText so that extra parameters can be set
*
* Returns: (transfer none): ethe #ClutterText used by #StLabel. The label
* is owned by the #StLabel and should not be unref'ed by the application.
*/
ClutterActor*
st_label_get_clutter_text (StLabel *label)
{
g_return_val_if_fail (ST_LABEL (label), NULL);
return label->priv->label;
}

76
src/st/st-label.h Normal file
View File

@ -0,0 +1,76 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-label.h: Plain label actor
*
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_LABEL_H__
#define __ST_LABEL_H__
G_BEGIN_DECLS
#include <st/st-widget.h>
#define ST_TYPE_LABEL (st_label_get_type ())
#define ST_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_LABEL, StLabel))
#define ST_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_LABEL))
#define ST_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_LABEL, StLabelClass))
#define ST_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_LABEL))
#define ST_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_LABEL, StLabelClass))
typedef struct _StLabel StLabel;
typedef struct _StLabelPrivate StLabelPrivate;
typedef struct _StLabelClass StLabelClass;
/**
* StLabel:
*
* The contents of this structure is private and should only be accessed using
* the provided API.
*/
struct _StLabel
{
/*< private >*/
StWidget parent_instance;
StLabelPrivate *priv;
};
struct _StLabelClass
{
StWidgetClass parent_class;
};
GType st_label_get_type (void) G_GNUC_CONST;
StWidget * st_label_new (const gchar *text);
G_CONST_RETURN gchar *st_label_get_text (StLabel *label);
void st_label_set_text (StLabel *label,
const gchar *text);
ClutterActor * st_label_get_clutter_text (StLabel *label);
G_END_DECLS
#endif /* __ST_LABEL_H__ */

12
src/st/st-marshal.list Normal file
View File

@ -0,0 +1,12 @@
VOID:OBJECT
VOID:VOID
VOID:PARAM
VOID:POINTER
VOID:UINT
VOID:UINT,UINT
VOID:OBJECT,OBJECT
VOID:STRING,OBJECT
VOID:OBJECT,OBJECT,INT,INT
VOID:OBJECT,FLOAT,FLOAT,INT,ENUM
VOID:FLOAT,FLOAT,INT,ENUM
VOID:FLOAT,FLOAT

112
src/st/st-private.c Normal file
View File

@ -0,0 +1,112 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "st-private.h"
/* Utility function to modify a child allocation box with respect to the
* x/y-fill child properties. Expects childbox to contain the available
* allocation space.
*/
void
_st_allocate_fill (ClutterActor *child,
ClutterActorBox *childbox,
StAlign x_alignment,
StAlign y_alignment,
gboolean x_fill,
gboolean y_fill)
{
gfloat natural_width, natural_height;
gfloat min_width, min_height;
gfloat child_width, child_height;
gfloat available_width, available_height;
ClutterRequestMode request;
ClutterActorBox allocation = { 0, };
gdouble x_align, y_align;
if (x_alignment == ST_ALIGN_START)
x_align = 0.0;
else if (x_alignment == ST_ALIGN_MIDDLE)
x_align = 0.5;
else
x_align = 1.0;
if (y_alignment == ST_ALIGN_START)
y_align = 0.0;
else if (y_alignment == ST_ALIGN_MIDDLE)
y_align = 0.5;
else
y_align = 1.0;
available_width = childbox->x2 - childbox->x1;
available_height = childbox->y2 - childbox->y1;
if (available_width < 0)
available_width = 0;
if (available_height < 0)
available_height = 0;
if (x_fill)
{
allocation.x1 = childbox->x1;
allocation.x2 = (int)(allocation.x1 + available_width);
}
if (y_fill)
{
allocation.y1 = childbox->y1;
allocation.y2 = (int)(allocation.y1 + available_height);
}
/* if we are filling horizontally and vertically then we're done */
if (x_fill && y_fill)
{
*childbox = allocation;
return;
}
request = CLUTTER_REQUEST_HEIGHT_FOR_WIDTH;
g_object_get (G_OBJECT (child), "request-mode", &request, NULL);
if (request == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
{
clutter_actor_get_preferred_width (child, available_height,
&min_width,
&natural_width);
child_width = CLAMP (natural_width, min_width, available_width);
clutter_actor_get_preferred_height (child, child_width,
&min_height,
&natural_height);
child_height = CLAMP (natural_height, min_height, available_height);
}
else
{
clutter_actor_get_preferred_height (child, available_width,
&min_height,
&natural_height);
child_height = CLAMP (natural_height, min_height, available_height);
clutter_actor_get_preferred_width (child, child_height,
&min_width,
&natural_width);
child_width = CLAMP (natural_width, min_width, available_width);
}
if (!x_fill)
{
allocation.x1 = childbox->x1 + (int)((available_width - child_width) * x_align);
allocation.x2 = allocation.x1 + (int) child_width;
}
if (!y_fill)
{
allocation.y1 = childbox->y1 + (int)((available_height - child_height) * y_align);
allocation.y2 = allocation.y1 + (int) child_height;
}
*childbox = allocation;
}

58
src/st/st-private.h Normal file
View File

@ -0,0 +1,58 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-private.h: Private declarations
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#ifndef __ST_PRIVATE_H__
#define __ST_PRIVATE_H__
#include <glib.h>
#include "st-widget.h"
#include "st-bin.h"
G_BEGIN_DECLS
#define I_(str) (g_intern_static_string ((str)))
#define ST_PARAM_READABLE \
(G_PARAM_READABLE | \
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
#define ST_PARAM_READWRITE \
(G_PARAM_READABLE | G_PARAM_WRITABLE | \
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
G_END_DECLS
ClutterActor *_st_widget_get_dnd_clone (StWidget *widget);
void _st_bin_get_align_factors (StBin *bin,
gdouble *x_align,
gdouble *y_align);
void _st_allocate_fill (ClutterActor *child,
ClutterActorBox *childbox,
StAlign x_align,
StAlign y_align,
gboolean x_fill,
gboolean y_fill);
#endif /* __ST_PRIVATE_H__ */

1077
src/st/st-scroll-bar.c Normal file

File diff suppressed because it is too large Load Diff

83
src/st/st-scroll-bar.h Normal file
View File

@ -0,0 +1,83 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-scroll-bar.h: Scroll bar actor
*
* Copyright 2008 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Chris Lord <chris@openedhand.com>
* Port to St by: Robert Staudinger <robsta@openedhand.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_SCROLL_BAR_H__
#define __ST_SCROLL_BAR_H__
#include <st/st-adjustment.h>
#include <st/st-bin.h>
G_BEGIN_DECLS
#define ST_TYPE_SCROLL_BAR (st_scroll_bar_get_type())
#define ST_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_BAR, StScrollBar))
#define ST_IS_SCROLL_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_BAR))
#define ST_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_BAR, StScrollBarClass))
#define ST_IS_SCROLL_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_BAR))
#define ST_SCROLL_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_BAR, StScrollBarClass))
typedef struct _StScrollBar StScrollBar;
typedef struct _StScrollBarPrivate StScrollBarPrivate;
typedef struct _StScrollBarClass StScrollBarClass;
/**
* StScrollBar:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
struct _StScrollBar
{
/*< private >*/
StBin parent_instance;
StScrollBarPrivate *priv;
};
struct _StScrollBarClass
{
StBinClass parent_class;
/* signals */
void (*scroll_start) (StScrollBar *bar);
void (*scroll_stop) (StScrollBar *bar);
};
GType st_scroll_bar_get_type (void) G_GNUC_CONST;
StWidget *st_scroll_bar_new (StAdjustment *adjustment);
void st_scroll_bar_set_adjustment (StScrollBar *bar,
StAdjustment *adjustment);
StAdjustment *st_scroll_bar_get_adjustment (StScrollBar *bar);
G_END_DECLS
#endif /* __ST_SCROLL_BAR_H__ */

844
src/st/st-scroll-view.c Normal file
View File

@ -0,0 +1,844 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-scroll-view.h: Container with scroll-bars
*
* Copyright 2008 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Chris Lord <chris@openedhand.com>
* Port to St by: Robert Staudinger <robsta@openedhand.com>
*
*/
/**
* SECTION:st-scroll-view
* @short_description: a container for scrollable children
*
* #StScrollView is a single child container for actors that implement
* #StScrollable. It provides scrollbars around the edge of the child to
* allow the user to move around the scrollable area.
*/
#include "st-scroll-view.h"
#include "st-marshal.h"
#include "st-scroll-bar.h"
#include "st-scrollable.h"
#include <clutter/clutter.h>
static void clutter_container_iface_init (ClutterContainerIface *iface);
static ClutterContainerIface *st_scroll_view_parent_iface = NULL;
G_DEFINE_TYPE_WITH_CODE (StScrollView, st_scroll_view, ST_TYPE_BIN,
G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
clutter_container_iface_init))
#define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
ST_TYPE_SCROLL_VIEW, \
StScrollViewPrivate))
/* Default width (or height - the narrow dimension) for the scrollbars*/
#define DEFAULT_SCROLLBAR_WIDTH 24
struct _StScrollViewPrivate
{
/* a pointer to the child; this is actually stored
* inside StBin:child, but we keep it to avoid
* calling st_bin_get_child() every time we need it
*/
ClutterActor *child;
ClutterActor *hscroll;
ClutterActor *vscroll;
gfloat row_size;
gfloat column_size;
gboolean row_size_set : 1;
gboolean column_size_set : 1;
guint mouse_scroll : 1;
};
enum {
PROP_0,
PROP_HSCROLL,
PROP_VSCROLL,
PROP_MOUSE_SCROLL
};
static void
st_scroll_view_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
StScrollViewPrivate *priv = ((StScrollView *) object)->priv;
switch (property_id)
{
case PROP_HSCROLL:
g_value_set_object (value, priv->hscroll);
break;
case PROP_VSCROLL:
g_value_set_object (value, priv->vscroll);
break;
case PROP_MOUSE_SCROLL:
g_value_set_boolean (value, priv->mouse_scroll);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
st_scroll_view_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id)
{
case PROP_MOUSE_SCROLL:
st_scroll_view_set_mouse_scrolling ((StScrollView *) object,
g_value_get_boolean (value));
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
}
static void
st_scroll_view_dispose (GObject *object)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (object)->priv;
priv->child = NULL;
if (priv->vscroll)
{
clutter_actor_unparent (priv->vscroll);
priv->vscroll = NULL;
}
if (priv->hscroll)
{
clutter_actor_unparent (priv->hscroll);
priv->hscroll = NULL;
}
G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object);
}
static void
st_scroll_view_finalize (GObject *object)
{
G_OBJECT_CLASS (st_scroll_view_parent_class)->finalize (object);
}
static void
st_scroll_view_paint (ClutterActor *actor)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
/* StBin will paint the child */
CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->paint (actor);
/* paint our custom children */
if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
clutter_actor_paint (priv->hscroll);
if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
clutter_actor_paint (priv->vscroll);
}
static void
st_scroll_view_pick (ClutterActor *actor,
const ClutterColor *color)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
/* Chain up so we get a bounding box pained (if we are reactive) */
CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->pick (actor, color);
/* paint our custom children */
if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
clutter_actor_paint (priv->hscroll);
if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
clutter_actor_paint (priv->vscroll);
}
static double
get_scrollbar_width (StScrollView *scroll_view)
{
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (scroll_view));
double result = DEFAULT_SCROLLBAR_WIDTH;
st_theme_node_get_length (theme_node, "scrollbar-width", FALSE, &result);
return result;
}
static double
get_scrollbar_height (StScrollView *scroll_view)
{
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (scroll_view));
double result = DEFAULT_SCROLLBAR_WIDTH;
st_theme_node_get_length (theme_node, "scrollbar-height", FALSE, &result);
return result;
}
static void
st_scroll_view_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
if (!priv->child)
return;
st_theme_node_adjust_for_height (theme_node, &for_height);
/* Our natural width is the natural width of the child */
clutter_actor_get_preferred_width (priv->child,
for_height,
NULL,
natural_width_p);
/* Add space for the scroll-bar if we can determine it will be necessary */
if ((for_height >= 0) && natural_width_p)
{
gfloat natural_height;
clutter_actor_get_preferred_height (priv->child, -1.0,
NULL,
&natural_height);
if (for_height < natural_height)
*natural_width_p += get_scrollbar_width (ST_SCROLL_VIEW (actor));
}
if (min_width_p)
*min_width_p = 0;
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
}
static void
st_scroll_view_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
if (!priv->child)
return;
st_theme_node_adjust_for_width (theme_node, &for_width);
/* Our natural height is the natural height of the child */
clutter_actor_get_preferred_height (priv->child,
for_width,
NULL,
natural_height_p);
/* Add space for the scroll-bar if we can determine it will be necessary */
if ((for_width >= 0) && natural_height_p)
{
gfloat natural_width;
clutter_actor_get_preferred_width (priv->child, -1.0,
NULL,
&natural_width);
if (for_width < natural_width)
*natural_height_p += get_scrollbar_height (ST_SCROLL_VIEW (actor));
}
if (min_height_p)
*min_height_p = 0;
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static void
st_scroll_view_allocate (ClutterActor *actor,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
ClutterActorBox content_box, child_box;
ClutterActorClass *parent_parent_class;
gfloat avail_width, avail_height, sb_width, sb_height;
StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
/* Chain up to the parent's parent class
*
* We do this because we do not want StBin to allocate the child, as we
* give it a different allocation later, depending on whether the scrollbars
* are visible
*/
parent_parent_class
= g_type_class_peek_parent (st_scroll_view_parent_class);
CLUTTER_ACTOR_CLASS (parent_parent_class)->
allocate (actor, box, flags);
st_theme_node_get_content_box (theme_node, box, &content_box);
avail_width = content_box.x2 - content_box.x1;
avail_height = content_box.y2 - content_box.y1;
sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor));
sb_height = get_scrollbar_width (ST_SCROLL_VIEW (actor));
if (!CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
sb_width = 0;
if (!CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
sb_height = 0;
/* Vertical scrollbar */
if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
{
child_box.x1 = content_box.x2 - sb_width;
child_box.y1 = content_box.y1;
child_box.x2 = content_box.x2;
child_box.y2 = content_box.y2 - sb_height;
clutter_actor_allocate (priv->vscroll, &child_box, flags);
}
/* Horizontal scrollbar */
if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
{
child_box.x1 = content_box.x1;
child_box.y1 = content_box.y2 - sb_height;
child_box.x2 = content_box.x2 - sb_width;
child_box.y2 = content_box.y2;
clutter_actor_allocate (priv->hscroll, &child_box, flags);
}
/* Child */
child_box.x1 = content_box.x1;
child_box.y1 = content_box.y1;
child_box.x2 = content_box.x2 - sb_width;
child_box.y2 = content_box.y2 - sb_height;
if (priv->child)
clutter_actor_allocate (priv->child, &child_box, flags);
}
static void
st_scroll_view_style_changed (StWidget *widget)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (widget)->priv;
st_widget_style_changed (ST_WIDGET (priv->hscroll));
st_widget_style_changed (ST_WIDGET (priv->vscroll));
ST_WIDGET_CLASS (st_scroll_view_parent_class)->style_changed (widget);
}
static gboolean
st_scroll_view_scroll_event (ClutterActor *self,
ClutterScrollEvent *event)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
gdouble lower, value, upper, step;
StAdjustment *vadjustment, *hadjustment;
/* don't handle scroll events if requested not to */
if (!priv->mouse_scroll)
return FALSE;
hadjustment = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->hscroll));
vadjustment = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->vscroll));
switch (event->direction)
{
case CLUTTER_SCROLL_UP:
case CLUTTER_SCROLL_DOWN:
if (vadjustment)
g_object_get (vadjustment,
"lower", &lower,
"step-increment", &step,
"value", &value,
"upper", &upper,
NULL);
else
return FALSE;
break;
case CLUTTER_SCROLL_LEFT:
case CLUTTER_SCROLL_RIGHT:
if (vadjustment)
g_object_get (hadjustment,
"lower", &lower,
"step-increment", &step,
"value", &value,
"upper", &upper,
NULL);
else
return FALSE;
break;
}
switch (event->direction)
{
case CLUTTER_SCROLL_UP:
if (value == lower)
return FALSE;
else
st_adjustment_set_value (vadjustment, value - step);
break;
case CLUTTER_SCROLL_DOWN:
if (value == upper)
return FALSE;
else
st_adjustment_set_value (vadjustment, value + step);
break;
case CLUTTER_SCROLL_LEFT:
if (value == lower)
return FALSE;
else
st_adjustment_set_value (hadjustment, value - step);
break;
case CLUTTER_SCROLL_RIGHT:
if (value == upper)
return FALSE;
else
st_adjustment_set_value (hadjustment, value + step);
break;
}
return TRUE;
}
static void
st_scroll_view_class_init (StScrollViewClass *klass)
{
GParamSpec *pspec;
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
g_type_class_add_private (klass, sizeof (StScrollViewPrivate));
object_class->get_property = st_scroll_view_get_property;
object_class->set_property = st_scroll_view_set_property;
object_class->dispose= st_scroll_view_dispose;
object_class->finalize = st_scroll_view_finalize;
actor_class->paint = st_scroll_view_paint;
actor_class->pick = st_scroll_view_pick;
actor_class->get_preferred_width = st_scroll_view_get_preferred_width;
actor_class->get_preferred_height = st_scroll_view_get_preferred_height;
actor_class->allocate = st_scroll_view_allocate;
actor_class->scroll_event = st_scroll_view_scroll_event;
widget_class->style_changed = st_scroll_view_style_changed;
g_object_class_install_property (object_class,
PROP_HSCROLL,
g_param_spec_object ("hscroll",
"StScrollBar",
"Horizontal scroll indicator",
ST_TYPE_SCROLL_BAR,
G_PARAM_READABLE));
g_object_class_install_property (object_class,
PROP_VSCROLL,
g_param_spec_object ("vscroll",
"StScrollBar",
"Vertical scroll indicator",
ST_TYPE_SCROLL_BAR,
G_PARAM_READABLE));
pspec = g_param_spec_boolean ("enable-mouse-scrolling",
"Enable Mouse Scrolling",
"Enable automatic mouse wheel scrolling",
TRUE,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_MOUSE_SCROLL,
pspec);
}
static void
child_adjustment_changed_cb (StAdjustment *adjustment,
ClutterActor *bar)
{
StScrollView *scroll;
gdouble lower, upper, page_size;
scroll = ST_SCROLL_VIEW (clutter_actor_get_parent (bar));
/* Determine if this scroll-bar should be visible */
st_adjustment_get_values (adjustment, NULL,
&lower, &upper,
NULL, NULL,
&page_size);
if ((upper - lower) > page_size)
clutter_actor_show (bar);
else
clutter_actor_hide (bar);
/* Request a resize */
clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll));
}
static void
child_hadjustment_notify_cb (GObject *gobject,
GParamSpec *arg1,
gpointer user_data)
{
StAdjustment *hadjust;
ClutterActor *actor = CLUTTER_ACTOR (gobject);
StScrollViewPrivate *priv = ST_SCROLL_VIEW (user_data)->priv;
hadjust = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->hscroll));
if (hadjust)
g_signal_handlers_disconnect_by_func (hadjust,
child_adjustment_changed_cb,
priv->hscroll);
st_scrollable_get_adjustments (ST_SCROLLABLE (actor), &hadjust, NULL);
if (hadjust)
{
/* Force scroll step if neede. */
if (priv->column_size_set)
{
g_object_set (hadjust,
"step-increment", priv->column_size,
NULL);
}
st_scroll_bar_set_adjustment (ST_SCROLL_BAR(priv->hscroll), hadjust);
g_signal_connect (hadjust, "changed", G_CALLBACK (
child_adjustment_changed_cb), priv->hscroll);
child_adjustment_changed_cb (hadjust, priv->hscroll);
}
}
static void
child_vadjustment_notify_cb (GObject *gobject,
GParamSpec *arg1,
gpointer user_data)
{
StAdjustment *vadjust;
ClutterActor *actor = CLUTTER_ACTOR (gobject);
StScrollViewPrivate *priv = ST_SCROLL_VIEW (user_data)->priv;
vadjust = st_scroll_bar_get_adjustment (ST_SCROLL_BAR(priv->vscroll));
if (vadjust)
g_signal_handlers_disconnect_by_func (vadjust,
child_adjustment_changed_cb,
priv->vscroll);
st_scrollable_get_adjustments (ST_SCROLLABLE(actor), NULL, &vadjust);
if (vadjust)
{
/* Force scroll step if neede. */
if (priv->row_size_set)
{
g_object_set (vadjust,
"step-increment", priv->row_size,
NULL);
}
st_scroll_bar_set_adjustment (ST_SCROLL_BAR(priv->vscroll), vadjust);
g_signal_connect (vadjust, "changed", G_CALLBACK (
child_adjustment_changed_cb), priv->vscroll);
child_adjustment_changed_cb (vadjust, priv->vscroll);
}
}
static void
st_scroll_view_init (StScrollView *self)
{
StScrollViewPrivate *priv = self->priv = SCROLL_VIEW_PRIVATE (self);
priv->hscroll = CLUTTER_ACTOR (st_scroll_bar_new (NULL));
priv->vscroll = g_object_new (ST_TYPE_SCROLL_BAR, "vertical", TRUE, NULL);
clutter_actor_set_parent (priv->hscroll, CLUTTER_ACTOR (self));
clutter_actor_set_parent (priv->vscroll, CLUTTER_ACTOR (self));
/* mouse scroll is enabled by default, so we also need to be reactive */
priv->mouse_scroll = TRUE;
g_object_set (G_OBJECT (self), "reactive", TRUE, NULL);
}
static void
st_scroll_view_add (ClutterContainer *container,
ClutterActor *actor)
{
StScrollView *self = ST_SCROLL_VIEW (container);
StScrollViewPrivate *priv = self->priv;
if (ST_IS_SCROLLABLE (actor))
{
priv->child = actor;
/* chain up to StBin::add() */
st_scroll_view_parent_iface->add (container, actor);
/* Get adjustments for scroll-bars */
g_signal_connect (actor, "notify::hadjustment",
G_CALLBACK (child_hadjustment_notify_cb),
container);
g_signal_connect (actor, "notify::vadjustment",
G_CALLBACK (child_vadjustment_notify_cb),
container);
child_hadjustment_notify_cb (G_OBJECT (actor), NULL, container);
child_vadjustment_notify_cb (G_OBJECT (actor), NULL, container);
}
else
{
g_warning ("Attempting to add an actor of type %s to "
"a StScrollView, but the actor does "
"not implement StScrollable.",
g_type_name (G_OBJECT_TYPE (actor)));
}
}
static void
st_scroll_view_remove (ClutterContainer *container,
ClutterActor *actor)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (container)->priv;
if (actor == priv->child)
{
g_object_ref (priv->child);
/* chain up to StBin::remove() */
st_scroll_view_parent_iface->remove (container, actor);
g_signal_handlers_disconnect_by_func (priv->child,
child_hadjustment_notify_cb,
container);
g_signal_handlers_disconnect_by_func (priv->child,
child_vadjustment_notify_cb,
container);
st_scrollable_set_adjustments ((StScrollable*) priv->child, NULL, NULL);
g_object_unref (priv->child);
priv->child = NULL;
}
}
static void
st_scroll_view_foreach_with_internals (ClutterContainer *container,
ClutterCallback callback,
gpointer user_data)
{
StScrollViewPrivate *priv = ST_SCROLL_VIEW (container)->priv;
if (priv->child != NULL)
callback (priv->child, user_data);
if (priv->hscroll != NULL)
callback (priv->hscroll, user_data);
if (priv->vscroll != NULL)
callback (priv->vscroll, user_data);
}
static void
clutter_container_iface_init (ClutterContainerIface *iface)
{
/* store a pointer to the StBin implementation of
* ClutterContainer so that we can chain up when
* overriding the methods
*/
st_scroll_view_parent_iface = g_type_interface_peek_parent (iface);
iface->add = st_scroll_view_add;
iface->remove = st_scroll_view_remove;
iface->foreach_with_internals = st_scroll_view_foreach_with_internals;
}
StWidget *
st_scroll_view_new (void)
{
return g_object_new (ST_TYPE_SCROLL_VIEW, NULL);
}
/**
* st_scroll_view_get_hscroll_bar:
* @scroll: a #StScrollView
*
* Gets the horizontal scrollbar of the scrollbiew
*
* Return value: (transfer none): the horizontal #StScrollbar
*/
ClutterActor *
st_scroll_view_get_hscroll_bar (StScrollView *scroll)
{
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
return scroll->priv->hscroll;
}
/**
* st_scroll_view_get_vscroll_bar:
* @scroll: a #StScrollView
*
* Gets the vertical scrollbar of the scrollbiew
*
* Return value: (transfer none): the vertical #StScrollbar
*/
ClutterActor *
st_scroll_view_get_vscroll_bar (StScrollView *scroll)
{
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL);
return scroll->priv->vscroll;
}
gfloat
st_scroll_view_get_column_size (StScrollView *scroll)
{
StAdjustment *adjustment;
gdouble column_size;
g_return_val_if_fail (scroll, 0);
adjustment = st_scroll_bar_get_adjustment (
ST_SCROLL_BAR (scroll->priv->hscroll));
g_object_get (adjustment,
"step-increment", &column_size,
NULL);
return column_size;
}
void
st_scroll_view_set_column_size (StScrollView *scroll,
gfloat column_size)
{
StAdjustment *adjustment;
g_return_if_fail (scroll);
if (column_size < 0)
{
scroll->priv->column_size_set = FALSE;
scroll->priv->column_size = -1;
}
else
{
scroll->priv->column_size_set = TRUE;
scroll->priv->column_size = column_size;
adjustment = st_scroll_bar_get_adjustment (
ST_SCROLL_BAR (scroll->priv->hscroll));
if (adjustment)
g_object_set (adjustment,
"step-increment", (gdouble) scroll->priv->column_size,
NULL);
}
}
gfloat
st_scroll_view_get_row_size (StScrollView *scroll)
{
StAdjustment *adjustment;
gdouble row_size;
g_return_val_if_fail (scroll, 0);
adjustment = st_scroll_bar_get_adjustment (
ST_SCROLL_BAR (scroll->priv->vscroll));
g_object_get (adjustment,
"step-increment", &row_size,
NULL);
return row_size;
}
void
st_scroll_view_set_row_size (StScrollView *scroll,
gfloat row_size)
{
StAdjustment *adjustment;
g_return_if_fail (scroll);
if (row_size < 0)
{
scroll->priv->row_size_set = FALSE;
scroll->priv->row_size = -1;
}
else
{
scroll->priv->row_size_set = TRUE;
scroll->priv->row_size = row_size;
adjustment = st_scroll_bar_get_adjustment (
ST_SCROLL_BAR (scroll->priv->vscroll));
if (adjustment)
g_object_set (adjustment,
"step-increment", (gdouble) scroll->priv->row_size,
NULL);
}
}
void
st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
gboolean enabled)
{
StScrollViewPrivate *priv;
g_return_if_fail (ST_IS_SCROLL_VIEW (scroll));
priv = ST_SCROLL_VIEW (scroll)->priv;
if (priv->mouse_scroll != enabled)
{
priv->mouse_scroll = enabled;
/* make sure we can receive mouse wheel events */
if (enabled)
clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE);
}
}
gboolean
st_scroll_view_get_mouse_scrolling (StScrollView *scroll)
{
StScrollViewPrivate *priv;
g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE);
priv = ST_SCROLL_VIEW (scroll)->priv;
return priv->mouse_scroll;
}

89
src/st/st-scroll-view.h Normal file
View File

@ -0,0 +1,89 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-scroll-view.h: Container with scroll-bars
*
* Copyright 2008 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Chris Lord <chris@openedhand.com>
* Port to St by: Robert Staudinger <robsta@openedhand.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_SCROLL_VIEW_H__
#define __ST_SCROLL_VIEW_H__
#include <st/st-bin.h>
G_BEGIN_DECLS
#define ST_TYPE_SCROLL_VIEW (st_scroll_view_get_type())
#define ST_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLL_VIEW, StScrollView))
#define ST_IS_SCROLL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLL_VIEW))
#define ST_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SCROLL_VIEW, StScrollViewClass))
#define ST_IS_SCROLL_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SCROLL_VIEW))
#define ST_SCROLL_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SCROLL_VIEW, StScrollViewClass))
typedef struct _StScrollView StScrollView;
typedef struct _StScrollViewPrivate StScrollViewPrivate;
typedef struct _StScrollViewClass StScrollViewClass;
/**
* StScrollView:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
struct _StScrollView
{
/*< private >*/
StBin parent_instance;
StScrollViewPrivate *priv;
};
struct _StScrollViewClass
{
StBinClass parent_class;
};
GType st_scroll_view_get_type (void) G_GNUC_CONST;
StWidget *st_scroll_view_new (void);
ClutterActor *st_scroll_view_get_hscroll_bar (StScrollView *scroll);
ClutterActor *st_scroll_view_get_vscroll_bar (StScrollView *scroll);
gfloat st_scroll_view_get_column_size (StScrollView *scroll);
void st_scroll_view_set_column_size (StScrollView *scroll,
gfloat column_size);
gfloat st_scroll_view_get_row_size (StScrollView *scroll);
void st_scroll_view_set_row_size (StScrollView *scroll,
gfloat row_size);
void st_scroll_view_set_mouse_scrolling (StScrollView *scroll,
gboolean enabled);
gboolean st_scroll_view_get_mouse_scrolling (StScrollView *scroll);
G_END_DECLS
#endif /* __ST_SCROLL_VIEW_H__ */

97
src/st/st-scrollable.c Normal file
View File

@ -0,0 +1,97 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-scrollable.c: Scrollable interface
*
* Copyright 2008 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Written by: Chris Lord <chris@openedhand.com>
* Port to St by: Robert Staudinger <robsta@openedhand.com>
*
*/
#include "st-scrollable.h"
static void
st_scrollable_base_init (gpointer g_iface)
{
static gboolean initialized = FALSE;
if (!initialized)
{
g_object_interface_install_property (g_iface,
g_param_spec_object ("hadjustment",
"StAdjustment",
"Horizontal adjustment",
ST_TYPE_ADJUSTMENT,
G_PARAM_READWRITE));
g_object_interface_install_property (g_iface,
g_param_spec_object ("vadjustment",
"StAdjustment",
"Vertical adjustment",
ST_TYPE_ADJUSTMENT,
G_PARAM_READWRITE));
initialized = TRUE;
}
}
GType
st_scrollable_get_type (void)
{
static GType type = 0;
if (type == 0)
{
static const GTypeInfo info =
{
sizeof (StScrollableInterface),
st_scrollable_base_init, /* base_init */
NULL,
};
type = g_type_register_static (G_TYPE_INTERFACE,
"StScrollable", &info, 0);
}
return type;
}
void
st_scrollable_set_adjustments (StScrollable *scrollable,
StAdjustment *hadjustment,
StAdjustment *vadjustment)
{
ST_SCROLLABLE_GET_INTERFACE (scrollable)->set_adjustments (scrollable,
hadjustment,
vadjustment);
}
/**
* st_scroll_bar_get_adjustments:
* @hadjustment: (transfer none) (out) (allow-none): location to store the horizontal adjustment, or %NULL
* @vadjustment: (transfer none) (out) (allow-none): location to store the vertical adjustment, or %NULL
*
* Gets the adjustment objects that store the offsets of the scrollable widget
* into its possible scrolling area.
*/
void
st_scrollable_get_adjustments (StScrollable *scrollable,
StAdjustment **hadjustment,
StAdjustment **vadjustment)
{
ST_SCROLLABLE_GET_INTERFACE (scrollable)->get_adjustments (scrollable,
hadjustment,
vadjustment);
}

70
src/st/st-scrollable.h Normal file
View File

@ -0,0 +1,70 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-scrollable.h: Scrollable interface
*
* Copyright 2008 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Chris Lord <chris@openedhand.com>
* Port to St by: Robert Staudinger <robsta@openedhand.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_SCROLLABLE_H__
#define __ST_SCROLLABLE_H__
#include <glib-object.h>
#include <st/st-adjustment.h>
G_BEGIN_DECLS
#define ST_TYPE_SCROLLABLE (st_scrollable_get_type ())
#define ST_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SCROLLABLE, StScrollable))
#define ST_IS_SCROLLABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SCROLLABLE))
#define ST_SCROLLABLE_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), ST_TYPE_SCROLLABLE, StScrollableInterface))
typedef struct _StScrollable StScrollable; /* Dummy object */
typedef struct _StScrollableInterface StScrollableInterface;
struct _StScrollableInterface
{
GTypeInterface parent;
void (* set_adjustments) (StScrollable *scrollable,
StAdjustment *hadjustment,
StAdjustment *vadjustment);
void (* get_adjustments) (StScrollable *scrollable,
StAdjustment **hadjustment,
StAdjustment **vadjustment);
};
GType st_scrollable_get_type (void) G_GNUC_CONST;
void st_scrollable_set_adjustments (StScrollable *scrollable,
StAdjustment *hadjustment,
StAdjustment *vadjustment);
void st_scrollable_get_adjustments (StScrollable *scrollable,
StAdjustment **hadjustment,
StAdjustment **vadjustment);
G_END_DECLS
#endif /* __ST_SCROLLABLE_H__ */

576
src/st/st-subtexture.c Normal file
View File

@ -0,0 +1,576 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-subtexture.h: Class to wrap a texture and "subframe" it.
* based on
* st-texture-frame.c: Expandible texture actor
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <cogl/cogl.h>
#include <clutter/clutter.h>
#include "st-subtexture.h"
enum
{
PROP_0,
PROP_PARENT_TEXTURE,
PROP_TOP,
PROP_LEFT,
PROP_WIDTH,
PROP_HEIGHT
};
G_DEFINE_TYPE (StSubtexture, st_subtexture, CLUTTER_TYPE_ACTOR);
#define ST_SUBTEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_SUBTEXTURE, StSubtexturePrivate))
struct _StSubtexturePrivate
{
ClutterTexture *parent_texture;
int left;
int top;
int width;
int height;
CoglHandle material;
};
static void
st_subtexture_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_width_p)
*min_width_p = 0;
if (natural_width_p)
*natural_width_p = 0;
}
else
{
if (min_width_p)
*min_width_p = priv->width;
if (natural_width_p)
*natural_width_p = priv->width;
}
}
static void
st_subtexture_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_height_p)
*min_height_p = 0;
if (natural_height_p)
*natural_height_p = 0;
}
else
{
if (min_height_p)
*min_height_p = priv->height;
if (natural_height_p)
*natural_height_p = priv->height;
}
}
static void
st_subtexture_realize (ClutterActor *self)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
if (priv->material != COGL_INVALID_HANDLE)
return;
priv->material = cogl_material_new ();
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
}
static void
st_subtexture_unrealize (ClutterActor *self)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
if (priv->material == COGL_INVALID_HANDLE)
return;
cogl_material_unref (priv->material);
priv->material = COGL_INVALID_HANDLE;
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
}
static void
st_subtexture_paint (ClutterActor *self)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (self)->priv;
CoglHandle cogl_texture = COGL_INVALID_HANDLE;
ClutterActorBox box = { 0, 0, 0, 0 };
gfloat tx1, ty1, tx2, ty2, tex_width, tex_height, width, height;
guint8 opacity;
/* no need to paint stuff if we don't have a texture */
if (G_UNLIKELY (priv->parent_texture == NULL))
return;
/* parent texture may have been hidden, so need to make sure it gets
* realized
*/
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
if (cogl_texture == COGL_INVALID_HANDLE)
return;
tex_width = cogl_texture_get_width (cogl_texture);
tex_height = cogl_texture_get_height (cogl_texture);
clutter_actor_get_allocation_box (self, &box);
width = box.x2 - box.x1;
height = box.y2 - box.y1;
tx1 = 1.0 * priv->left / tex_width;
ty1 = 1.0 * priv->top / tex_height;
tx2 = 1.0 * (priv->left + priv->width) / tex_width;
ty2 = 1.0 * (priv->top + priv->height) / tex_height;
opacity = clutter_actor_get_paint_opacity (self);
g_assert (priv->material != COGL_INVALID_HANDLE);
/* set the source material using the parent texture's COGL handle */
cogl_material_set_color4ub (priv->material, 255, 255, 255, opacity);
cogl_material_set_layer (priv->material, 0, cogl_texture);
cogl_set_source (priv->material);
cogl_rectangle_with_texture_coords (0,0, (float) width, (float) height,
tx1, ty1, tx2, ty2);
}
static inline void
st_subtexture_set_frame_internal (StSubtexture *frame,
int left,
int top,
int width,
int height)
{
StSubtexturePrivate *priv = frame->priv;
GObject *gobject = G_OBJECT (frame);
gboolean changed = FALSE;
g_object_freeze_notify (gobject);
if (priv->top != top)
{
priv->top = top;
g_object_notify (gobject, "top");
changed = TRUE;
}
if (priv->left != left)
{
priv->left = left;
g_object_notify (gobject, "left");
changed = TRUE;
}
if (priv->width != width)
{
priv->width = width;
g_object_notify (gobject, "width");
changed = TRUE;
}
if (priv->height != height)
{
priv->height = height;
g_object_notify (gobject, "height");
changed = TRUE;
}
if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
g_object_thaw_notify (gobject);
}
static void
st_subtexture_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StSubtexture *frame = ST_SUBTEXTURE (gobject);
StSubtexturePrivate *priv = frame->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
st_subtexture_set_parent_texture (frame,
g_value_get_object (value));
break;
case PROP_TOP:
st_subtexture_set_frame_internal (frame,
priv->left,
g_value_get_int (value),
priv->width,
priv->height);
break;
case PROP_LEFT:
st_subtexture_set_frame_internal (frame,
g_value_get_int (value),
priv->top,
priv->width,
priv->height);
break;
case PROP_WIDTH:
st_subtexture_set_frame_internal (frame,
priv->left,
priv->top,
g_value_get_int (value),
priv->height);
break;
case PROP_HEIGHT:
st_subtexture_set_frame_internal (frame,
priv->left,
priv->top,
priv->width,
g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_subtexture_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (gobject)->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
g_value_set_object (value, priv->parent_texture);
break;
case PROP_LEFT:
g_value_set_int (value, priv->left);
break;
case PROP_TOP:
g_value_set_int (value, priv->top);
break;
case PROP_WIDTH:
g_value_set_int (value, priv->width);
break;
case PROP_HEIGHT:
g_value_set_int (value, priv->height);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_subtexture_dispose (GObject *gobject)
{
StSubtexturePrivate *priv = ST_SUBTEXTURE (gobject)->priv;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
}
if (priv->material)
{
cogl_material_unref (priv->material);
priv->material = COGL_INVALID_HANDLE;
}
G_OBJECT_CLASS (st_subtexture_parent_class)->dispose (gobject);
}
static void
st_subtexture_class_init (StSubtextureClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (gobject_class, sizeof (StSubtexturePrivate));
actor_class->get_preferred_width =
st_subtexture_get_preferred_width;
actor_class->get_preferred_height =
st_subtexture_get_preferred_height;
actor_class->realize = st_subtexture_realize;
actor_class->unrealize = st_subtexture_unrealize;
actor_class->paint = st_subtexture_paint;
gobject_class->set_property = st_subtexture_set_property;
gobject_class->get_property = st_subtexture_get_property;
gobject_class->dispose = st_subtexture_dispose;
pspec = g_param_spec_object ("parent-texture",
"Parent Texture",
"The parent ClutterTexture",
CLUTTER_TYPE_TEXTURE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT);
g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
pspec = g_param_spec_int ("left",
"Left",
"Left offset",
0, G_MAXINT,
0,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
pspec = g_param_spec_int ("top",
"Top",
"Top offset",
0, G_MAXINT,
0,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TOP, pspec);
pspec = g_param_spec_int ("width",
"Width",
"Width",
0, G_MAXINT,
0,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_WIDTH, pspec);
pspec = g_param_spec_int ("height",
"Height",
"Height",
0, G_MAXINT,
0,
G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_HEIGHT, pspec);
}
static void
st_subtexture_init (StSubtexture *self)
{
StSubtexturePrivate *priv;
self->priv = priv = ST_SUBTEXTURE_GET_PRIVATE (self);
priv->material = COGL_INVALID_HANDLE;
}
/**
* st_subtexture_new:
* @texture: a #ClutterTexture or %NULL
* @left: left
* @top: top
* @width: width
* @height: height
*
* A #StSubtexture is a specialized texture that efficiently clones
* an area of the given @texture while keeping preserving portions of the
* same texture.
*
* A #StSubtexture can be used to make a rectangular texture fit a
* given size without stretching its borders.
*
* Return value: the newly created #StSubtexture
*/
ClutterActor*
st_subtexture_new (ClutterTexture *texture,
gint left,
gint top,
gint width,
gint height)
{
g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
return g_object_new (ST_TYPE_SUBTEXTURE,
"parent-texture", texture,
"top", top,
"left", left,
"width", width,
"height", height,
NULL);
}
/**
* st_subtexture_get_parent_texture:
* @frame: A #StSubtexture
*
* Return the texture used by the #StSubtexture
*
* Returns: (transfer none): a #ClutterTexture owned by the #StSubtexture
*/
ClutterTexture *
st_subtexture_get_parent_texture (StSubtexture *frame)
{
g_return_val_if_fail (ST_IS_SUBTEXTURE (frame), NULL);
return frame->priv->parent_texture;
}
/**
* st_subtexture_set_parent_texture:
* @frame: A #StSubtexture
* @texture: A #ClutterTexture
*
* Set the #ClutterTexture used by this #StSubtexture
*
*/
void
st_subtexture_set_parent_texture (StSubtexture *frame,
ClutterTexture *texture)
{
StSubtexturePrivate *priv;
gboolean was_visible;
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
priv = frame->priv;
was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
if (priv->parent_texture == texture)
return;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
if (was_visible)
clutter_actor_hide (CLUTTER_ACTOR (frame));
}
if (texture)
{
priv->parent_texture = g_object_ref (texture);
if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
clutter_actor_show (CLUTTER_ACTOR (frame));
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
g_object_notify (G_OBJECT (frame), "parent-texture");
}
/**
* st_subtexture_set_frame:
* @frame: A #StSubtexture
* @left: left
* @top: top
* @width: width
* @height: height
*
* Set the frame of the subtexture
*
*/
void
st_subtexture_set_frame (StSubtexture *frame,
gint left,
gint top,
gint width,
gint height)
{
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
st_subtexture_set_frame_internal (frame, left, top, width, height);
}
/**
* st_subtexture_get_frame:
* @frame: A #StSubtexture
* @left: left
* @top: top
* @width: width
* @height: height
*
* Retrieve the current frame.
*
*/
void
st_subtexture_get_frame (StSubtexture *frame,
gint *left,
gint *top,
gint *width,
gint *height)
{
StSubtexturePrivate *priv;
g_return_if_fail (ST_IS_SUBTEXTURE (frame));
priv = frame->priv;
if (top)
*top = priv->top;
if (left)
*left = priv->left;
if (width)
*width = priv->width;
if (height)
*height = priv->height;
}

97
src/st/st-subtexture.h Normal file
View File

@ -0,0 +1,97 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-subtexture.h: Class to wrap a texture and "subframe" it.
*
* Based on
* st-texture-frame.h: Expandible texture actor
*
* Copyright 2007, 2008 OpenedHand Ltd
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_SUBTEXTURE_H__
#define __ST_SUBTEXTURE_H__
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_SUBTEXTURE (st_subtexture_get_type ())
#define ST_SUBTEXTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_SUBTEXTURE, StSubtexture))
#define ST_SUBTEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_SUBTEXTURE, StSubtextureClass))
#define ST_IS_SUBTEXTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_SUBTEXTURE))
#define ST_IS_SUBTEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_SUBTEXTURE))
#define ST_SUBTEXTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_SUBTEXTURE, StSubtextureClass))
typedef struct _StSubtexture StSubtexture;
typedef struct _StSubtexturePrivate StSubtexturePrivate;
typedef struct _StSubtextureClass StSubtextureClass;
/**
* StSubtexture:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
struct _StSubtexture
{
/*< private >*/
ClutterActor parent_instance;
StSubtexturePrivate *priv;
};
struct _StSubtextureClass
{
ClutterActorClass parent_class;
/* padding for future expansion */
void (*_st_box_1) (void);
void (*_st_box_2) (void);
void (*_st_box_3) (void);
void (*_st_box_4) (void);
};
GType st_subtexture_get_type (void) G_GNUC_CONST;
ClutterActor * st_subtexture_new (ClutterTexture *texture,
gint top,
gint left,
gint width,
gint height);
void st_subtexture_set_parent_texture (StSubtexture *frame,
ClutterTexture *texture);
ClutterTexture *st_subtexture_get_parent_texture (StSubtexture *frame);
void st_subtexture_set_frame (StSubtexture *frame,
gint top,
gint left,
gint width,
gint height);
void st_subtexture_get_frame (StSubtexture *frame,
gint *top,
gint *left,
gint *width,
gint *height);
G_END_DECLS
#endif /* __ST_SUBTEXTURE_H__ */

805
src/st/st-table-child.c Normal file
View File

@ -0,0 +1,805 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-table-child.h: Table child implementation
*
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas.wood@intel.com>
*
*/
#include "st-private.h"
#include "st-table-child.h"
#include "st-table-private.h"
#include <st/st-widget.h>
#include <st/st-table.h>
/*
* ClutterChildMeta Implementation
*/
/**
* SECTION:st-table-child
* @short_description: The child property store for #StTable
*
* The #ClutterChildMeta implementation for the #StTable container widget.
*
*/
enum {
CHILD_PROP_0,
CHILD_PROP_COL,
CHILD_PROP_ROW,
CHILD_PROP_COL_SPAN,
CHILD_PROP_ROW_SPAN,
CHILD_PROP_X_EXPAND,
CHILD_PROP_Y_EXPAND,
CHILD_PROP_X_ALIGN,
CHILD_PROP_Y_ALIGN,
CHILD_PROP_X_FILL,
CHILD_PROP_Y_FILL,
CHILD_PROP_ALLOCATE_HIDDEN,
};
G_DEFINE_TYPE (StTableChild, st_table_child, CLUTTER_TYPE_CHILD_META);
static void
table_child_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StTableChild *child = ST_TABLE_CHILD (gobject);
StTable *table = ST_TABLE (CLUTTER_CHILD_META(gobject)->container);
switch (prop_id)
{
case CHILD_PROP_COL:
child->col = g_value_get_int (value);
_st_table_update_row_col (table, -1, child->col);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_ROW:
child->row = g_value_get_int (value);
_st_table_update_row_col (table, child->row, -1);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_COL_SPAN:
child->col_span = g_value_get_int (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_ROW_SPAN:
child->row_span = g_value_get_int (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_X_EXPAND:
child->x_expand = g_value_get_boolean (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_Y_EXPAND:
child->y_expand = g_value_get_boolean (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_X_ALIGN:
child->x_align = g_value_get_double (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_Y_ALIGN:
child->y_align = g_value_get_double (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_X_FILL:
child->x_fill = g_value_get_boolean (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_Y_FILL:
child->y_fill = g_value_get_boolean (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
case CHILD_PROP_ALLOCATE_HIDDEN:
child->allocate_hidden = g_value_get_boolean (value);
clutter_actor_queue_relayout (CLUTTER_ACTOR (table));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
table_child_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StTableChild *child = ST_TABLE_CHILD (gobject);
switch (prop_id)
{
case CHILD_PROP_COL:
g_value_set_int (value, child->col);
break;
case CHILD_PROP_ROW:
g_value_set_int (value, child->row);
break;
case CHILD_PROP_COL_SPAN:
g_value_set_int (value, child->col_span);
break;
case CHILD_PROP_ROW_SPAN:
g_value_set_int (value, child->row_span);
break;
case CHILD_PROP_X_EXPAND:
g_value_set_boolean (value, child->x_expand);
break;
case CHILD_PROP_Y_EXPAND:
g_value_set_boolean (value, child->y_expand);
break;
case CHILD_PROP_X_ALIGN:
g_value_set_double (value, child->x_align);
break;
case CHILD_PROP_Y_ALIGN:
g_value_set_double (value, child->y_align);
break;
case CHILD_PROP_X_FILL:
g_value_set_boolean (value, child->x_fill);
break;
case CHILD_PROP_Y_FILL:
g_value_set_boolean (value, child->y_fill);
break;
case CHILD_PROP_ALLOCATE_HIDDEN:
g_value_set_boolean (value, child->allocate_hidden);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_table_child_class_init (StTableChildClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GParamSpec *pspec;
gobject_class->set_property = table_child_set_property;
gobject_class->get_property = table_child_get_property;
pspec = g_param_spec_int ("col",
"Column Number",
"The column the widget resides in",
0, G_MAXINT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_COL, pspec);
pspec = g_param_spec_int ("row",
"Row Number",
"The row the widget resides in",
0, G_MAXINT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_ROW, pspec);
pspec = g_param_spec_int ("row-span",
"Row Span",
"The number of rows the widget should span",
1, G_MAXINT,
1,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_ROW_SPAN, pspec);
pspec = g_param_spec_int ("col-span",
"Column Span",
"The number of columns the widget should span",
1, G_MAXINT,
1,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_COL_SPAN, pspec);
pspec = g_param_spec_boolean ("x-expand",
"X Expand",
"Whether the child should receive priority "
"when the container is allocating spare space "
"on the horizontal axis",
TRUE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_X_EXPAND, pspec);
pspec = g_param_spec_boolean ("y-expand",
"Y Expand",
"Whether the child should receive priority "
"when the container is allocating spare space "
"on the vertical axis",
TRUE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_Y_EXPAND, pspec);
pspec = g_param_spec_double ("x-align",
"X Alignment",
"X alignment of the widget within the cell",
0, 1,
0.5,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_X_ALIGN, pspec);
pspec = g_param_spec_double ("y-align",
"Y Alignment",
"Y alignment of the widget within the cell",
0, 1,
0.5,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_Y_ALIGN, pspec);
pspec = g_param_spec_boolean ("x-fill",
"X Fill",
"Whether the child should be allocated its "
"entire available space, or whether it should "
"be squashed and aligned.",
TRUE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_X_FILL, pspec);
pspec = g_param_spec_boolean ("y-fill",
"Y Fill",
"Whether the child should be allocated its "
"entire available space, or whether it should "
"be squashed and aligned.",
TRUE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_Y_FILL, pspec);
pspec = g_param_spec_boolean ("allocate-hidden",
"Allocate Hidden",
"Whether the child should be allocate even "
"if it is hidden",
TRUE,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, CHILD_PROP_ALLOCATE_HIDDEN, pspec);
}
static void
st_table_child_init (StTableChild *self)
{
self->col_span = 1;
self->row_span = 1;
self->x_align = 0.5;
self->y_align = 0.5;
self->x_expand = TRUE;
self->y_expand = TRUE;
self->x_fill = TRUE;
self->y_fill = TRUE;
self->allocate_hidden = TRUE;
}
static StTableChild*
get_child_meta (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
meta = (StTableChild*) clutter_container_get_child_meta (CLUTTER_CONTAINER (table), child);
return meta;
}
/**
* st_table_child_get_col_span:
* @table: an #StTable
* @child: a #ClutterActor
*
* Get the column span of the child. Defaults to 1.
*
* Returns: the column span of the child
*/
gint
st_table_child_get_col_span (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), 0);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
meta = get_child_meta (table, child);
return meta->col_span;
}
/**
* st_table_child_set_col_span:
* @table: An #StTable
* @child: An #ClutterActor
* @span: The number of columns to span
*
* Set the column span of the child.
*
*/
void
st_table_child_set_col_span (StTable *table,
ClutterActor *child,
gint span)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
g_return_if_fail (span > 1);
meta = get_child_meta (table, child);
meta->col_span = span;
clutter_actor_queue_relayout (child);
}
/**
* st_table_child_get_row_span:
* @table: A #StTable
* @child: A #ClutterActor
*
* Get the row span of the child. Defaults to 1.
*
* Returns: the row span of the child
*/
gint
st_table_child_get_row_span (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), 0);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
meta = get_child_meta (table, child);
return meta->row_span;
}
/**
* st_table_child_set_row_span:
* @table: A #StTable
* @child: A #ClutterActor
* @span: the number of rows to span
*
* Set the row span of the child.
*
*/
void
st_table_child_set_row_span (StTable *table,
ClutterActor *child,
gint span)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
g_return_if_fail (span > 1);
meta = get_child_meta (table, child);
meta->row_span = span;
clutter_actor_queue_relayout (child);
}
/**
* st_table_child_get_x_fill:
* @table: A #StTable
* @child: A #ClutterActor
*
* Get the x-fill state of the child
*
* Returns: #TRUE if the child is set to x-fill
*/
gboolean
st_table_child_get_x_fill (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), 0);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
meta = get_child_meta (table, child);
return meta->x_fill;
}
/**
* st_table_child_set_x_fill:
* @table: A #StTable
* @child: A #ClutterActor
* @fill: the fill state
*
* Set the fill state of the child on the x-axis. This will cause the child to
* be allocated the maximum available space.
*
*/
void
st_table_child_set_x_fill (StTable *table,
ClutterActor *child,
gboolean fill)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
meta = get_child_meta (table, child);
meta->x_fill = fill;
clutter_actor_queue_relayout (child);
}
/**
* st_table_child_get_y_fill:
* @table: A #StTable
* @child: A #ClutterActor
*
* Get the y-fill state of the child
*
* Returns: #TRUE if the child is set to y-fill
*/
gboolean
st_table_child_get_y_fill (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), 0);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
meta = get_child_meta (table, child);
return meta->y_fill;
}
/**
* st_table_child_set_y_fill:
* @table: A #StTable
* @child: A #ClutterActor
* @fill: the fill state
*
* Set the fill state of the child on the y-axis. This will cause the child to
* be allocated the maximum available space.
*
*/
void
st_table_child_set_y_fill (StTable *table,
ClutterActor *child,
gboolean fill)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
meta = get_child_meta (table, child);
meta->y_fill = fill;
clutter_actor_queue_relayout (child);
}
/**
* st_table_child_get_x_expand:
* @table: A #StTable
* @child: A #ClutterActor
*
* Get the x-expand property of the child
*
* Returns: #TRUE if the child is set to x-expand
*/
gboolean
st_table_child_get_x_expand (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), 0);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
meta = get_child_meta (table, child);
return meta->x_expand;
}
/**
* st_table_child_set_x_expand:
* @table: A #StTable
* @child: A #ClutterActor
* @expand: the new value of the x expand child property
*
* Set x-expand on the child. This causes the column which the child
* resides in to be allocated any extra space if the allocation of the table is
* larger than the preferred size.
*
*/
void
st_table_child_set_x_expand (StTable *table,
ClutterActor *child,
gboolean expand)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
meta = get_child_meta (table, child);
meta->x_expand = expand;
clutter_actor_queue_relayout (child);
}
/**
* st_table_child_set_y_expand:
* @table: A #StTable
* @child: A #ClutterActor
* @expand: the new value of the y-expand child property
*
* Set y-expand on the child. This causes the row which the child
* resides in to be allocated any extra space if the allocation of the table is
* larger than the preferred size.
*
*/
void
st_table_child_set_y_expand (StTable *table,
ClutterActor *child,
gboolean expand)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
meta = get_child_meta (table, child);
meta->y_expand = expand;
clutter_actor_queue_relayout (child);
}
/**
* st_table_child_get_y_expand:
* @table: A #StTable
* @child: A #ClutterActor
*
* Get the y-expand property of the child.
*
* Returns: #TRUE if the child is set to y-expand
*/
gboolean
st_table_child_get_y_expand (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), 0);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
meta = get_child_meta (table, child);
return meta->y_expand;
}
/**
* st_table_child_get_x_align:
* @table: A #StTable
* @child: A #ClutterActor
*
* Get the x-align value of the child
*
* Returns: An #StAlign value
*/
StAlign
st_table_child_get_x_align (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), 0);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
meta = get_child_meta (table, child);
if (meta->x_align == 0.0)
return ST_ALIGN_START;
else if (meta->x_align == 1.0)
return ST_ALIGN_END;
else
return ST_ALIGN_MIDDLE;
}
/**
* st_table_child_set_x_align:
* @table: A #StTable
* @child: A #ClutterActor
* @align: A #StAlign value
*
* Set the alignment of the child within its cell. This will only have an effect
* if the the x-fill property is FALSE.
*
*/
void
st_table_child_set_x_align (StTable *table,
ClutterActor *child,
StAlign align)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
meta = get_child_meta (table, child);
switch (align)
{
case ST_ALIGN_START:
meta->x_align = 0.0;
break;
case ST_ALIGN_MIDDLE:
meta->x_align = 0.5;
break;
case ST_ALIGN_END:
meta->x_align = 1.0;
break;
}
clutter_actor_queue_relayout (child);
}
/**
* st_table_child_get_y_align:
* @table: A #StTable
* @child: A #ClutterActor
*
* Get the y-align value of the child
*
* Returns: An #StAlign value
*/
StAlign
st_table_child_get_y_align (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), 0);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), 0);
meta = get_child_meta (table, child);
if (meta->y_align == 0.0)
return ST_ALIGN_START;
else if (meta->y_align == 1.0)
return ST_ALIGN_END;
else
return ST_ALIGN_MIDDLE;
}
/**
* st_table_child_set_y_align:
* @table: A #StTable
* @child: A #ClutterActor
* @align: A #StAlign value
*
* Set the value of the y-align property. This will only have an effect if
* y-fill value is set to FALSE.
*
*/
void
st_table_child_set_y_align (StTable *table,
ClutterActor *child,
StAlign align)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
meta = get_child_meta (table, child);
switch (align)
{
case ST_ALIGN_START:
meta->y_align = 0.0;
break;
case ST_ALIGN_MIDDLE:
meta->y_align = 0.5;
break;
case ST_ALIGN_END:
meta->y_align = 1.0;
break;
}
clutter_actor_queue_relayout (child);
}
/**
* st_table_child_set_allocate_hidden:
* @table: A #StTable
* @child: A #ClutterActor
* @value: #TRUE if the actor should be allocated when hidden
*
* Set whether the child should be allocate even if it is hidden
*/
void
st_table_child_set_allocate_hidden (StTable *table,
ClutterActor *child,
gboolean value)
{
StTableChild *meta;
g_return_if_fail (ST_IS_TABLE (table));
g_return_if_fail (CLUTTER_IS_ACTOR (child));
meta = get_child_meta (table, child);
if (meta->allocate_hidden != value)
{
meta->allocate_hidden = value;
clutter_actor_queue_relayout (child);
g_object_notify (G_OBJECT (meta), "allocate-hidden");
}
}
/**
* st_table_child_get_allocate_hidden:
* @table: A #StTable
* @child: A #ClutterActor
*
* Determine if the child is allocated even if it is hidden
*
* Returns: #TRUE if the actor is allocated when hidden
*/
gboolean
st_table_child_get_allocate_hidden (StTable *table,
ClutterActor *child)
{
StTableChild *meta;
g_return_val_if_fail (ST_IS_TABLE (table), TRUE);
g_return_val_if_fail (CLUTTER_IS_ACTOR (child), TRUE);
meta = get_child_meta (table, child);
return meta->allocate_hidden;
}

129
src/st/st-table-child.h Normal file
View File

@ -0,0 +1,129 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-table-child.h: Table child implementation
*
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TABLE_CHILD_H__
#define __ST_TABLE_CHILD_H__
#include <st/st-types.h>
#include <st/st-widget.h>
#include <st/st-table.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_TABLE_CHILD (st_table_child_get_type ())
#define ST_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TABLE_CHILD, StTableChild))
#define ST_IS_TABLE_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TABLE_CHILD))
#define ST_TABLE_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TABLE_CHILD, StTableChildClass))
#define ST_IS_TABLE_CHILD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TABLE_CHILD))
#define ST_TABLE_CHILD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TABLE_CHILD, StTableChildClass))
typedef struct _StTableChild StTableChild;
typedef struct _StTableChildClass StTableChildClass;
/**
* StTableChild:
*
* The contents of the this structure are private and should only be accessed
* through the public API.
*/
struct _StTableChild
{
/*< private >*/
ClutterChildMeta parent_instance;
gint col;
gint row;
gint col_span;
gint row_span;
gdouble x_align;
gdouble y_align;
guint allocate_hidden : 1;
guint x_expand : 1;
guint y_expand : 1;
guint x_fill : 1;
guint y_fill : 1;
};
struct _StTableChildClass
{
ClutterChildMetaClass parent_class;
};
GType st_table_child_get_type (void) G_GNUC_CONST;
gint st_table_child_get_col_span (StTable *table,
ClutterActor *child);
void st_table_child_set_col_span (StTable *table,
ClutterActor *child,
gint span);
gint st_table_child_get_row_span (StTable *table,
ClutterActor *child);
void st_table_child_set_row_span (StTable *table,
ClutterActor *child,
gint span);
gboolean st_table_child_get_x_fill (StTable *table,
ClutterActor *child);
void st_table_child_set_x_fill (StTable *table,
ClutterActor *child,
gboolean fill);
gboolean st_table_child_get_y_fill (StTable *table,
ClutterActor *child);
void st_table_child_set_y_fill (StTable *table,
ClutterActor *child,
gboolean fill);
gboolean st_table_child_get_x_expand (StTable *table,
ClutterActor *child);
void st_table_child_set_x_expand (StTable *table,
ClutterActor *child,
gboolean expand);
gboolean st_table_child_get_y_expand (StTable *table,
ClutterActor *child);
void st_table_child_set_y_expand (StTable *table,
ClutterActor *child,
gboolean expand);
StAlign st_table_child_get_x_align (StTable *table,
ClutterActor *child);
void st_table_child_set_x_align (StTable *table,
ClutterActor *child,
StAlign align);
StAlign st_table_child_get_y_align (StTable *table,
ClutterActor *child);
void st_table_child_set_y_align (StTable *table,
ClutterActor *child,
StAlign align);
void st_table_child_set_allocate_hidden (StTable *table,
ClutterActor *child,
gboolean value);
gboolean st_table_child_get_allocate_hidden (StTable *table,
ClutterActor *child);
G_END_DECLS
#endif /* __ST_TABLE_H__ */

36
src/st/st-table-private.h Normal file
View File

@ -0,0 +1,36 @@
/*
* st-private-private.h: Private declarations for StTable
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#ifndef __ST_TABLE_PRIVATE_H__
#define __ST_TABLE_PRIVATE_H__
#include "st-table.h"
G_BEGIN_DECLS
void _st_table_update_row_col (StTable *table,
gint row,
gint col);
G_END_DECLS
#endif /* __ST_TABLE_PRIVATE_H__ */

1257
src/st/st-table.c Normal file

File diff suppressed because it is too large Load Diff

95
src/st/st-table.h Normal file
View File

@ -0,0 +1,95 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-table.h: Table layout widget
*
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TABLE_H__
#define __ST_TABLE_H__
#include <st/st-types.h>
#include <st/st-widget.h>
G_BEGIN_DECLS
/**
* StTableChildOptions:
* @ST_KEEP_ASPECT_RATIO: whether to respect the widget's aspect ratio
* @ST_X_EXPAND: whether to allocate extra space on the widget's x-axis
* @ST_Y_EXPAND: whether to allocate extra space on the widget's y-axis
* @ST_X_FILL: whether to stretch the child to fill the cell horizontally
* @ST_Y_FILL: whether to stretch the child to fill the cell vertically
*
* Denotes the child properties an StTable child will have.
*/
typedef enum
{
ST_KEEP_ASPECT_RATIO = 1 << 0,
ST_X_EXPAND = 1 << 1,
ST_Y_EXPAND = 1 << 2,
ST_X_FILL = 1 << 3,
ST_Y_FILL = 1 << 4
} StTableChildOptions;
#define ST_TYPE_TABLE (st_table_get_type ())
#define ST_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TABLE, StTable))
#define ST_IS_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TABLE))
#define ST_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TABLE, StTableClass))
#define ST_IS_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TABLE))
#define ST_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TABLE, StTableClass))
typedef struct _StTable StTable;
typedef struct _StTablePrivate StTablePrivate;
typedef struct _StTableClass StTableClass;
/**
* StTable:
*
* The contents of this structure is private and should only be accessed using
* the provided API.
*/
struct _StTable
{
/*< private >*/
StWidget parent_instance;
StTablePrivate *priv;
};
struct _StTableClass
{
StWidgetClass parent_class;
};
GType st_table_get_type (void) G_GNUC_CONST;
StWidget* st_table_new (void);
gint st_table_get_row_count (StTable *table);
gint st_table_get_column_count (StTable *table);
G_END_DECLS
#endif /* __ST_TABLE_H__ */

451
src/st/st-texture-cache.c Normal file
View File

@ -0,0 +1,451 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-widget.h: Base class for St actors
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
/**
* SECTION:st-texture-cache
* @short_description: A per-process store to cache textures
*
* #StTextureCache allows an application to re-use an previously loaded
* textures.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#include <glib-object.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <string.h>
#include "st-texture-cache.h"
#include "st-marshal.h"
#include "st-private.h"
#include "st-subtexture.h"
G_DEFINE_TYPE (StTextureCache, st_texture_cache, G_TYPE_OBJECT)
#define TEXTURE_CACHE_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_TEXTURE_CACHE, StTextureCachePrivate))
typedef struct _StTextureCachePrivate StTextureCachePrivate;
struct _StTextureCachePrivate
{
GHashTable *cache;
};
typedef struct FinalizedClosure
{
gchar *path;
StTextureCache *cache;
} FinalizedClosure;
enum
{
PROP_0,
};
static StTextureCache* __cache_singleton = NULL;
/*
* Convention: posX with a value of -1 indicates whole texture
*/
typedef struct StTextureCacheItem {
char filename[256];
int width, height;
int posX, posY;
ClutterActor *ptr;
} StTextureCacheItem;
static StTextureCacheItem *
st_texture_cache_item_new (void)
{
return g_slice_new0 (StTextureCacheItem);
}
static void
st_texture_cache_item_free (StTextureCacheItem *item)
{
g_slice_free (StTextureCacheItem, item);
}
static void
st_texture_cache_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
st_texture_cache_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
st_texture_cache_dispose (GObject *object)
{
if (G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose)
G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
}
static void
st_texture_cache_finalize (GObject *object)
{
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(object);
if (priv->cache)
{
g_hash_table_unref (priv->cache);
priv->cache = NULL;
}
G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
}
static void
st_texture_cache_class_init (StTextureCacheClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (klass, sizeof (StTextureCachePrivate));
object_class->get_property = st_texture_cache_get_property;
object_class->set_property = st_texture_cache_set_property;
object_class->dispose = st_texture_cache_dispose;
object_class->finalize = st_texture_cache_finalize;
}
static void
st_texture_cache_init (StTextureCache *self)
{
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
priv->cache = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
NULL);
}
/**
* st_texture_cache_get_default:
*
* Returns the default texture cache. This is owned by St and should not be
* unreferenced or freed.
*
* Returns: (transfer none): a StTextureCache
*/
StTextureCache*
st_texture_cache_get_default (void)
{
if (G_UNLIKELY (__cache_singleton == NULL))
__cache_singleton = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
return __cache_singleton;
}
#if 0
static void
on_texure_finalized (gpointer data,
GObject *where_the_object_was)
{
FinalizedClosure *closure = (FinalizedClosure *) data;
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(closure->cache);
g_hash_table_remove (priv->cache, closure->path);
g_free(closure->path);
g_free(closure);
}
#endif
/**
* st_texture_cache_get_size:
* @self: A #StTextureCache
*
* Returns the number of items in the texture cache
*
* Returns: the current size of the cache
*/
gint
st_texture_cache_get_size (StTextureCache *self)
{
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
return g_hash_table_size (priv->cache);
}
static void
add_texture_to_cache (StTextureCache *self,
const gchar *path,
StTextureCacheItem *item)
{
/* FinalizedClosure *closure; */
StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
g_hash_table_insert (priv->cache, g_strdup (path), item);
#if 0
/* Make sure we can remove from hash */
closure = g_new0 (FinalizedClosure, 1);
closure->path = g_strdup (path);
closure->cache = self;
g_object_weak_ref (G_OBJECT (res), on_texure_finalized, closure);
#endif
}
/* NOTE: you should unref the returned texture when not needed */
/**
* st_texture_cache_get_texture:
* @self: A #StTextureCache
* @path: A path to a image file
*
* Create a new ClutterTexture with the specified image. Adds the image to the
* cache if the image had not been previously loaded. Subsequent calls with
* the same image path will return a new ClutterTexture with the previously
* loaded image.
*
* Returns: (transfer none): a newly created ClutterTexture
*/
ClutterTexture*
st_texture_cache_get_texture (StTextureCache *self,
const gchar *path)
{
ClutterActor *texture;
CoglHandle *handle;
StTextureCachePrivate *priv;
StTextureCacheItem *item;
g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
g_return_val_if_fail (path != NULL, NULL);
priv = TEXTURE_CACHE_PRIVATE (self);
item = g_hash_table_lookup (priv->cache, path);
if (item && item->posX != -1)
{
GError *err = NULL;
/*
* We have a cache hit, but it's for a partial texture. The only
* sane option is to read it from disk and just don't cache it
* at all.
*/
return CLUTTER_TEXTURE(clutter_texture_new_from_file(path, &err));
}
if (!item)
{
GError *err = NULL;
item = st_texture_cache_item_new ();
item->posX = -1;
item->posY = -1;
item->ptr = clutter_texture_new_from_file (path, &err);
clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
&item->width, &item->height);
if (!item->ptr)
{
if (err)
{
g_warning ("Error loading image: %s", err->message);
g_error_free (err);
}
return NULL;
}
add_texture_to_cache (self, path, item);
}
texture = clutter_texture_new ();
handle = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (item->ptr));
clutter_texture_set_cogl_texture ((ClutterTexture*) texture, handle);
return (ClutterTexture*) texture;
}
/**
* st_texture_cache_get_actor:
* @self: A #StTextureCache
* @path: A path to a image file
*
* Create a new ClutterSubTexture with the specified image. Adds the image to the
* cache if the image had not been previously loaded. Subsequent calls with
* the same image path will return a new ClutterTexture with the previously
* loaded image.
*
* Use this function if all you need is an actor for drawing.
*
* Returns: (transfer none): a newly created ClutterTexture
*/
ClutterActor*
st_texture_cache_get_actor (StTextureCache *self,
const gchar *path)
{
StTextureCachePrivate *priv;
StTextureCacheItem *item;
GError *err = NULL;
g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
g_return_val_if_fail (path != NULL, NULL);
priv = TEXTURE_CACHE_PRIVATE (self);
item = g_hash_table_lookup (priv->cache, path);
if (item)
{
int posX = item->posX;
int posY = item->posY;
if (posX == -1)
posX = 0;
if (posY == -1)
posY = 0;
return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), posX, posY,
item->width, item->height);
}
item = st_texture_cache_item_new ();
item->posX = -1;
item->posY = -1;
item->ptr = clutter_texture_new_from_file (path, &err);
clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
&item->width, &item->height);
if (!item->ptr)
{
if (err)
{
g_warning ("Error loading image: %s", err->message);
g_error_free (err);
}
return NULL;
}
add_texture_to_cache (self, path, item);
return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), 0, 0, item->width,
item->height);
}
void
st_texture_cache_load_cache (StTextureCache *self,
const gchar *filename)
{
FILE *file;
StTextureCacheItem *element, head;
int ret;
ClutterActor *actor;
GError *error = NULL;
StTextureCachePrivate *priv;
g_return_if_fail (ST_IS_TEXTURE_CACHE (self));
g_return_if_fail (filename != NULL);
priv = TEXTURE_CACHE_PRIVATE (self);
file = fopen(filename, "rm");
if (!file)
return;
ret = fread (&head, sizeof(StTextureCacheItem), 1, file);
if (ret < 0)
{
fclose (file);
return;
}
/* check if we already if this texture in the cache */
if (g_hash_table_lookup (priv->cache, head.filename))
{
/* skip it, we're done */
fclose (file);
return;
}
actor = clutter_texture_new_from_file (head.filename, &error);
if (error)
{
g_critical (G_STRLOC ": Error opening cache image file: %s",
error->message);
g_clear_error (&error);
fclose (file);
return;
}
element = st_texture_cache_item_new ();
element->posX = -1;
element->posY = -1;
element->ptr = actor;
strncpy (element->filename, head.filename, 256);
clutter_texture_get_base_size (CLUTTER_TEXTURE (element->ptr),
&element->width, &element->height);
g_hash_table_insert (priv->cache, element->filename, element);
while (!feof (file))
{
element = st_texture_cache_item_new ();
ret = fread (element, sizeof (StTextureCacheItem), 1, file);
if (ret < 1)
{
/* end of file */
st_texture_cache_item_free (element);
break;
}
element->ptr = actor;
if (g_hash_table_lookup (priv->cache, element->filename))
{
/* file is already in the cache.... */
st_texture_cache_item_free (element);
} else {
g_hash_table_insert (priv->cache, element->filename, element);
}
}
}

96
src/st/st-texture-cache.h Normal file
View File

@ -0,0 +1,96 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-texture-cache.h: Cached textures object
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef _ST_TEXTURE_CACHE
#define _ST_TEXTURE_CACHE
#include <glib-object.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_TEXTURE_CACHE st_texture_cache_get_type()
#define ST_TEXTURE_CACHE(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
ST_TYPE_TEXTURE_CACHE, StTextureCache))
#define ST_TEXTURE_CACHE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
#define ST_IS_TEXTURE_CACHE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
ST_TYPE_TEXTURE_CACHE))
#define ST_IS_TEXTURE_CACHE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
ST_TYPE_TEXTURE_CACHE))
#define ST_TEXTURE_CACHE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
/**
* StTextureCache:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
typedef struct {
/*< private >*/
GObject parent;
} StTextureCache;
typedef struct {
GObjectClass parent_class;
void (* loaded) (StTextureCache *self,
const gchar *path,
ClutterTexture *texture);
void (* error_loading) (StTextureCache *self,
GError *error);
} StTextureCacheClass;
GType st_texture_cache_get_type (void);
StTextureCache* st_texture_cache_get_default (void);
ClutterTexture* st_texture_cache_get_texture (StTextureCache *self,
const gchar *path);
ClutterActor* st_texture_cache_get_actor (StTextureCache *self,
const gchar *path);
gint st_texture_cache_get_size (StTextureCache *self);
void st_texture_cache_load_cache (StTextureCache *self,
const char *filename);
G_END_DECLS
#endif /* _ST_TEXTURE_CACHE */

621
src/st/st-texture-frame.c Normal file
View File

@ -0,0 +1,621 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-texture-frame.h: Expandible texture actor
*
* Copyright 2007 OpenedHand
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
/**
* SECTION:st-texture-frame
* @short_description: Stretch a texture to fit the entire allocation
*
* #StTextureFrame
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <cogl/cogl.h>
#include "st-texture-frame.h"
#include "st-private.h"
enum
{
PROP_0,
PROP_PARENT_TEXTURE,
PROP_TOP,
PROP_RIGHT,
PROP_BOTTOM,
PROP_LEFT
};
G_DEFINE_TYPE (StTextureFrame, st_texture_frame, CLUTTER_TYPE_ACTOR);
#define ST_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFramePrivate))
struct _StTextureFramePrivate
{
ClutterTexture *parent_texture;
gfloat top;
gfloat right;
gfloat bottom;
gfloat left;
};
static void
st_texture_frame_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_width_p)
*min_width_p = 0;
if (natural_width_p)
*natural_width_p = 0;
}
else
{
ClutterActorClass *klass;
/* by directly querying the parent texture's class implementation
* we are going around any override mechanism the parent texture
* might have in place, and we ask directly for the original
* preferred width
*/
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture),
for_height,
min_width_p,
natural_width_p);
}
}
static void
st_texture_frame_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_height_p)
*min_height_p = 0;
if (natural_height_p)
*natural_height_p = 0;
}
else
{
ClutterActorClass *klass;
/* by directly querying the parent texture's class implementation
* we are going around any override mechanism the parent texture
* might have in place, and we ask directly for the original
* preferred height
*/
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture),
for_width,
min_height_p,
natural_height_p);
}
}
static void
st_texture_frame_paint (ClutterActor *self)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
CoglHandle cogl_texture = COGL_INVALID_HANDLE;
CoglHandle cogl_material = COGL_INVALID_HANDLE;
ClutterActorBox box = { 0, };
gfloat width, height;
gfloat tex_width, tex_height;
gfloat ex, ey;
gfloat tx1, ty1, tx2, ty2;
guint8 opacity;
/* no need to paint stuff if we don't have a texture */
if (G_UNLIKELY (priv->parent_texture == NULL))
return;
/* parent texture may have been hidden, so need to make sure it gets
* realized
*/
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
if (cogl_texture == COGL_INVALID_HANDLE)
return;
cogl_material = clutter_texture_get_cogl_material (priv->parent_texture);
if (cogl_material == COGL_INVALID_HANDLE)
return;
tex_width = cogl_texture_get_width (cogl_texture);
tex_height = cogl_texture_get_height (cogl_texture);
clutter_actor_get_allocation_box (self, &box);
width = box.x2 - box.x1;
height = box.y2 - box.y1;
tx1 = priv->left / tex_width;
tx2 = (tex_width - priv->right) / tex_width;
ty1 = priv->top / tex_height;
ty2 = (tex_height - priv->bottom) / tex_height;
ex = width - priv->right;
if (ex < 0)
ex = priv->right; /* FIXME ? */
ey = height - priv->bottom;
if (ey < 0)
ey = priv->bottom; /* FIXME ? */
opacity = clutter_actor_get_paint_opacity (self);
/* Paint using the parent texture's material. It should already have
the cogl texture set as the first layer */
/* NB: for correct blending we need set a preumultiplied color here: */
cogl_material_set_color4ub (cogl_material,
opacity, opacity, opacity, opacity);
cogl_set_source (cogl_material);
{
GLfloat rectangles[] =
{
/* top left corner */
0, 0, priv->left, priv->top,
0.0, 0.0,
tx1, ty1,
/* top middle */
priv->left, 0, ex, priv->top,
tx1, 0.0,
tx2, ty1,
/* top right */
ex, 0, width, priv->top,
tx2, 0.0,
1.0, ty1,
/* mid left */
0, priv->top, priv->left, ey,
0.0, ty1,
tx1, ty2,
/* center */
priv->left, priv->top, ex, ey,
tx1, ty1,
tx2, ty2,
/* mid right */
ex, priv->top, width, ey,
tx2, ty1,
1.0, ty2,
/* bottom left */
0, ey, priv->left, height,
0.0, ty2,
tx1, 1.0,
/* bottom center */
priv->left, ey, ex, height,
tx1, ty2,
tx2, 1.0,
/* bottom right */
ex, ey, width, height,
tx2, ty2,
1.0, 1.0
};
cogl_rectangles_with_texture_coords (rectangles, 9);
}
}
static inline void
st_texture_frame_set_frame_internal (StTextureFrame *frame,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left)
{
StTextureFramePrivate *priv = frame->priv;
GObject *gobject = G_OBJECT (frame);
gboolean changed = FALSE;
g_object_freeze_notify (gobject);
if (priv->top != top)
{
priv->top = top;
g_object_notify (gobject, "top");
changed = TRUE;
}
if (priv->right != right)
{
priv->right = right;
g_object_notify (gobject, "right");
changed = TRUE;
}
if (priv->bottom != bottom)
{
priv->bottom = bottom;
g_object_notify (gobject, "bottom");
changed = TRUE;
}
if (priv->left != left)
{
priv->left = left;
g_object_notify (gobject, "left");
changed = TRUE;
}
if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
g_object_thaw_notify (gobject);
}
static void
st_texture_frame_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StTextureFrame *frame = ST_TEXTURE_FRAME (gobject);
StTextureFramePrivate *priv = frame->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
st_texture_frame_set_parent_texture (frame,
g_value_get_object (value));
break;
case PROP_TOP:
st_texture_frame_set_frame_internal (frame,
g_value_get_float (value),
priv->right,
priv->bottom,
priv->left);
break;
case PROP_RIGHT:
st_texture_frame_set_frame_internal (frame,
priv->top,
g_value_get_float (value),
priv->bottom,
priv->left);
break;
case PROP_BOTTOM:
st_texture_frame_set_frame_internal (frame,
priv->top,
priv->right,
g_value_get_float (value),
priv->left);
break;
case PROP_LEFT:
st_texture_frame_set_frame_internal (frame,
priv->top,
priv->right,
priv->bottom,
g_value_get_float (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_texture_frame_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
g_value_set_object (value, priv->parent_texture);
break;
case PROP_LEFT:
g_value_set_float (value, priv->left);
break;
case PROP_TOP:
g_value_set_float (value, priv->top);
break;
case PROP_RIGHT:
g_value_set_float (value, priv->right);
break;
case PROP_BOTTOM:
g_value_set_float (value, priv->bottom);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_texture_frame_dispose (GObject *gobject)
{
StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
}
G_OBJECT_CLASS (st_texture_frame_parent_class)->dispose (gobject);
}
static void
st_texture_frame_class_init (StTextureFrameClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (gobject_class, sizeof (StTextureFramePrivate));
actor_class->get_preferred_width =
st_texture_frame_get_preferred_width;
actor_class->get_preferred_height =
st_texture_frame_get_preferred_height;
actor_class->paint = st_texture_frame_paint;
gobject_class->set_property = st_texture_frame_set_property;
gobject_class->get_property = st_texture_frame_get_property;
gobject_class->dispose = st_texture_frame_dispose;
pspec = g_param_spec_object ("parent-texture",
"Parent Texture",
"The parent ClutterTexture",
CLUTTER_TYPE_TEXTURE,
ST_PARAM_READWRITE |
G_PARAM_CONSTRUCT);
g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
pspec = g_param_spec_float ("left",
"Left",
"Left offset",
0, G_MAXFLOAT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
pspec = g_param_spec_float ("top",
"Top",
"Top offset",
0, G_MAXFLOAT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TOP, pspec);
pspec = g_param_spec_float ("bottom",
"Bottom",
"Bottom offset",
0, G_MAXFLOAT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec);
pspec = g_param_spec_float ("right",
"Right",
"Right offset",
0, G_MAXFLOAT,
0,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_RIGHT, pspec);
}
static void
st_texture_frame_init (StTextureFrame *self)
{
StTextureFramePrivate *priv;
self->priv = priv = ST_TEXTURE_FRAME_GET_PRIVATE (self);
}
/**
* st_texture_frame_new:
* @texture: a #ClutterTexture or %NULL
* @left: left margin preserving its content
* @top: top margin preserving its content
* @right: right margin preserving its content
* @bottom: bottom margin preserving its content
*
* A #StTextureFrame is a specialized texture that efficiently clones
* an area of the given @texture while keeping preserving portions of the
* same texture.
*
* A #StTextureFrame can be used to make a rectangular texture fit a
* given size without stretching its borders.
*
* Return value: the newly created #StTextureFrame
*/
ClutterActor*
st_texture_frame_new (ClutterTexture *texture,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left)
{
g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
return g_object_new (ST_TYPE_TEXTURE_FRAME,
"parent-texture", texture,
"top", top,
"right", right,
"bottom", bottom,
"left", left,
NULL);
}
/**
* st_texture_frame_get_parent_texture:
* @frame: A #StTextureFrame
*
* Return the texture used by the #StTextureFrame
*
* Returns: (transfer none): a #ClutterTexture owned by the #StTextureFrame
*/
ClutterTexture *
st_texture_frame_get_parent_texture (StTextureFrame *frame)
{
g_return_val_if_fail (ST_IS_TEXTURE_FRAME (frame), NULL);
return frame->priv->parent_texture;
}
/**
* st_texture_frame_set_parent_texture:
* @frame: A #StTextureFrame
* @texture: A #ClutterTexture
*
* Set the #ClutterTexture used by this #StTextureFrame
*
*/
void
st_texture_frame_set_parent_texture (StTextureFrame *frame,
ClutterTexture *texture)
{
StTextureFramePrivate *priv;
gboolean was_visible;
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
priv = frame->priv;
was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
if (priv->parent_texture == texture)
return;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
if (was_visible)
clutter_actor_hide (CLUTTER_ACTOR (frame));
}
if (texture)
{
priv->parent_texture = g_object_ref_sink (texture);
if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
clutter_actor_show (CLUTTER_ACTOR (frame));
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
g_object_notify (G_OBJECT (frame), "parent-texture");
}
/**
* st_texture_frame_set_frame:
* @frame: A #StTextureFrame
* @top: width of the top slice
* @right: width of the right slice
* @bottom: width of the bottom slice
* @left: width of the left slice
*
* Set the slice lines of the specified frame. The slices are calculated as
* widths from the edge of the frame.
*
*/
void
st_texture_frame_set_frame (StTextureFrame *frame,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left)
{
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
st_texture_frame_set_frame_internal (frame, top, right, bottom, left);
}
/**
* st_texture_frame_get_frame:
* @frame: A #StTextureFrame
* @top: width of the top slice
* @right: width of the right slice
* @bottom: width of the bottom slice
* @left: width of the left slice
*
* Retrieve the current slice lines from the specified frame.
*
*/
void
st_texture_frame_get_frame (StTextureFrame *frame,
gfloat *top,
gfloat *right,
gfloat *bottom,
gfloat *left)
{
StTextureFramePrivate *priv;
g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
priv = frame->priv;
if (top)
*top = priv->top;
if (right)
*right = priv->right;
if (bottom)
*bottom = priv->bottom;
if (left)
*left = priv->left;
}

94
src/st/st-texture-frame.h Normal file
View File

@ -0,0 +1,94 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-texture-frame.h: Expandible texture actor
*
* Copyright 2007, 2008 OpenedHand Ltd
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TEXTURE_FRAME_H__
#define __ST_TEXTURE_FRAME_H__
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define ST_TYPE_TEXTURE_FRAME (st_texture_frame_get_type ())
#define ST_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrame))
#define ST_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass))
#define ST_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TEXTURE_FRAME))
#define ST_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TEXTURE_FRAME))
#define ST_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass))
typedef struct _StTextureFrame StTextureFrame;
typedef struct _StTextureFramePrivate StTextureFramePrivate;
typedef struct _StTextureFrameClass StTextureFrameClass;
/**
* StTextureFrame:
*
* The contents of this structure are private and should only be accessed
* through the public API.
*/
struct _StTextureFrame
{
/*< private >*/
ClutterActor parent_instance;
StTextureFramePrivate *priv;
};
struct _StTextureFrameClass
{
ClutterActorClass parent_class;
/* padding for future expansion */
void (*_clutter_box_1) (void);
void (*_clutter_box_2) (void);
void (*_clutter_box_3) (void);
void (*_clutter_box_4) (void);
};
GType st_texture_frame_get_type (void) G_GNUC_CONST;
ClutterActor * st_texture_frame_new (ClutterTexture *texture,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left);
void st_texture_frame_set_parent_texture (StTextureFrame *frame,
ClutterTexture *texture);
ClutterTexture *st_texture_frame_get_parent_texture (StTextureFrame *frame);
void st_texture_frame_set_frame (StTextureFrame *frame,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left);
void st_texture_frame_get_frame (StTextureFrame *frame,
gfloat *top,
gfloat *right,
gfloat *bottom,
gfloat *left);
G_END_DECLS
#endif /* __ST_TEXTURE_FRAME_H__ */

287
src/st/st-theme-context.c Normal file
View File

@ -0,0 +1,287 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include <config.h>
#include "st-theme.h"
#include "st-theme-context.h"
struct _StThemeContext {
GObject parent;
double resolution;
PangoFontDescription *font;
StThemeNode *root_node;
StTheme *theme;
};
struct _StThemeContextClass {
GObjectClass parent_class;
};
#define DEFAULT_RESOLUTION 96.
#define DEFAULT_FONT "sans-serif 10"
enum
{
CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0, };
G_DEFINE_TYPE (StThemeContext, st_theme_context, G_TYPE_OBJECT)
static void
st_theme_context_finalize (GObject *object)
{
StThemeContext *context = ST_THEME_CONTEXT (object);
if (context->root_node)
g_object_unref (context->root_node);
if (context->theme)
g_object_unref (context->theme);
pango_font_description_free (context->font);
G_OBJECT_CLASS (st_theme_context_parent_class)->finalize (object);
}
static void
st_theme_context_class_init (StThemeContextClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = st_theme_context_finalize;
signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, /* no default handler slot */
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
st_theme_context_init (StThemeContext *context)
{
context->resolution = DEFAULT_RESOLUTION;
context->font = pango_font_description_from_string (DEFAULT_FONT);
}
/**
* st_theme_context_new:
*
* Create a new theme context not associated with any #ClutterStage.
* This can be useful in testing scenarios, or if using StThemeContext
* with something other than #ClutterActor objects, but you generally
* should use st_theme_context_get_for_stage() instead.
*/
StThemeContext *
st_theme_context_new (void)
{
StThemeContext *context;
context = g_object_new (ST_TYPE_THEME_CONTEXT, NULL);
return context;
}
static void
on_stage_destroy (ClutterStage *stage)
{
StThemeContext *context = st_theme_context_get_for_stage (stage);
g_object_set_data (G_OBJECT (stage), "st-theme-context", NULL);
g_object_unref (context);
}
static void
st_theme_context_changed (StThemeContext *context)
{
StThemeNode *old_root = context->root_node;
context->root_node = NULL;
g_signal_emit (context, signals[CHANGED], 0);
if (old_root)
g_object_unref (old_root);
}
/**
* st_theme_context_get_for_stage:
* @stage: a #ClutterStage
*
* Gets a singleton theme context associated with the stage.
*
* Return value: (transfer none): the singleton theme context for the stage
*/
StThemeContext *
st_theme_context_get_for_stage (ClutterStage *stage)
{
StThemeContext *context;
g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL);
context = g_object_get_data (G_OBJECT (stage), "st-theme-context");
if (context)
return context;
context = st_theme_context_new ();
g_object_set_data (G_OBJECT (stage), "st-theme-context", context);
g_signal_connect (stage, "destroy",
G_CALLBACK (on_stage_destroy), NULL);
return context;
}
/**
* st_theme_context_set_theme:
* @context: a #StThemeContext
*
* Sets the default set of theme stylesheets for the context. This theme will
* be used for the root node and for nodes descending from it, unless some other
* style is explicitely specified.
*/
void
st_theme_context_set_theme (StThemeContext *context,
StTheme *theme)
{
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
g_return_if_fail (theme == NULL || ST_IS_THEME (theme));
if (context->theme != theme)
{
if (context->theme)
g_object_unref (context->theme);
context->theme = theme;
if (context->theme)
g_object_ref (context->theme);
st_theme_context_changed (context);
}
}
/**
* st_theme_context_get_theme:
* @context: a #StThemeContext
*
* Gets the default theme for the context. See st_theme_context_set_theme()
*
* Return value: (transfer none): the default theme for the context
*/
StTheme *
st_theme_context_get_theme (StThemeContext *context)
{
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
return context->theme;
}
/**
* st_theme_context_set_resolution:
* @context: a #StThemeContext
* @resolution: resolution of the context (number of pixels in an "inch")
*
* Sets the resolution of the theme context. This is the scale factor
* used to convert between points and the length units pt, in, and cm.
* This does not necessarily need to correspond to the actual number
* resolution of the device. A value of 72. means that points and
* pixels are identical. The default value is 96.
*/
void
st_theme_context_set_resolution (StThemeContext *context,
double resolution)
{
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
if (resolution == context->resolution)
return;
context->resolution = resolution;
st_theme_context_changed (context);
}
/**
* st_theme_context_set_resolution:
* @context: a #StThemeContext
*
* Gets the current resolution of the theme context.
* See st_theme_context_set_resolution().
*
* Return value: the resolution (in dots-per-"inch")
*/
double
st_theme_context_get_resolution (StThemeContext *context)
{
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), DEFAULT_RESOLUTION);
return context->resolution;
}
/**
* st_theme_context_set_font:
* @context: a #StThemeContext
* @font: the default font for theme context
*
* Sets the default font for the theme context. This is the font that
* is inherited by the root node of the tree of theme nodes. If the
* font is not overriden, then this font will be used. If the font is
* partially modified (for example, with 'font-size: 110%', then that
* modification is based on this font.
*/
void
st_theme_context_set_font (StThemeContext *context,
const PangoFontDescription *font)
{
g_return_if_fail (ST_IS_THEME_CONTEXT (context));
g_return_if_fail (font != NULL);
if (context->font == font ||
pango_font_description_equal (context->font, font))
return;
pango_font_description_free (context->font);
context->font = pango_font_description_copy (font);
st_theme_context_changed (context);
}
/**
* st_theme_context_get_font:
* @context: a #StThemeContext
*
* Gets the default font for the theme context. See st_theme_context_set_font().
*
* Return value: the default font for the theme context.
*/
const PangoFontDescription *
st_theme_context_get_font (StThemeContext *context)
{
g_return_val_if_fail (ST_IS_THEME_CONTEXT (context), NULL);
return context->font;
}
/**
* st_theme_context_get_root_node:
* @context: a #StThemeContext
*
* Gets the root node of the tree of theme style nodes that associated with this
* context. For the node tree associated with a stage, this node represents
* styles applied to the stage itself.
*
* Return value: (transfer none): the root node of the context's style tree
*/
StThemeNode *
st_theme_context_get_root_node (StThemeContext *context)
{
if (context->root_node == NULL)
context->root_node = st_theme_node_new (context, NULL, context->theme,
G_TYPE_NONE, NULL, NULL, NULL, NULL);
return context->root_node;
}

50
src/st/st-theme-context.h Normal file
View File

@ -0,0 +1,50 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __ST_THEME_CONTEXT_H__
#define __ST_THEME_CONTEXT_H__
#include <clutter/clutter.h>
#include <pango/pango.h>
#include "st-theme-node.h"
G_BEGIN_DECLS
/**
* SECTION:StThemeContext
* @short_description: holds global information about a tree of styled objects
*
* #StThemeContext is responsible for managing information global to a tree of styled objects,
* such as the set of stylesheets or the default font. In normal usage, a #StThemeContext
* is bound to a #ClutterStage; a singleton #StThemeContext can be obtained for a #ClutterStage
* by using st_theme_context_get_for_stage().
*/
typedef struct _StThemeContextClass StThemeContextClass;
#define ST_TYPE_THEME_CONTEXT (st_theme_context_get_type ())
#define ST_THEME_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME_CONTEXT, StThemeContext))
#define ST_THEME_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME_CONTEXT, StThemeContextClass))
#define ST_IS_THEME_CONTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME_CONTEXT))
#define ST_IS_THEME_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME_CONTEXT))
#define ST_THEME_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME_CONTEXT, StThemeContextClass))
GType st_theme_context_get_type (void) G_GNUC_CONST;
StThemeContext *st_theme_context_new (void);
StThemeContext *st_theme_context_get_for_stage (ClutterStage *stage);
void st_theme_context_set_theme (StThemeContext *context,
StTheme *theme);
StTheme * st_theme_context_get_theme (StThemeContext *context);
void st_theme_context_set_resolution (StThemeContext *context,
gdouble resolution);
double st_theme_context_get_resolution (StThemeContext *context);
void st_theme_context_set_font (StThemeContext *context,
const PangoFontDescription *font);
const PangoFontDescription *st_theme_context_get_font (StThemeContext *context);
StThemeNode * st_theme_context_get_root_node (StThemeContext *context);
G_END_DECLS
#endif /* __ST_THEME_CONTEXT_H__ */

2146
src/st/st-theme-node.c Normal file

File diff suppressed because it is too large Load Diff

153
src/st/st-theme-node.h Normal file
View File

@ -0,0 +1,153 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __ST_THEME_NODE_H__
#define __ST_THEME_NODE_H__
#include <clutter/clutter.h>
#include "st-border-image.h"
G_BEGIN_DECLS
/**
* SECTION:StThemeNode
* @short_description: style information for one node in a tree of themed objects
*
* A #StThemeNode represents the CSS style information (the set of CSS properties) for one
* node in a tree of themed objects. In typical usage, it represents the style information
* for a single #ClutterActor. A #StThemeNode is immutable: attributes such as the
* CSS classes for the node are passed in at construction. If the attributes of the node
* or any parent node change, the node should be discarded and a new node created.
* #StThemeNode has generic accessors to look up properties by name and specific
* accessors for standard CSS properties that add caching and handling of various
* details of the CSS specification. #StThemeNode also has convenience functions to help
* in implementing a #ClutterActor with borders and padding.
*/
typedef struct _StTheme StTheme;
typedef struct _StThemeContext StThemeContext;
typedef struct _StThemeNode StThemeNode;
typedef struct _StThemeNodeClass StThemeNodeClass;
#define ST_TYPE_THEME_NODE (st_theme_node_get_type ())
#define ST_THEME_NODE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME_NODE, StThemeNode))
#define ST_THEME_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME_NODE, StThemeNodeClass))
#define ST_IS_THEME_NODE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME_NODE))
#define ST_IS_THEME_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME_NODE))
#define ST_THEME_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME_NODE, StThemeNodeClass))
typedef enum {
ST_SIDE_TOP,
ST_SIDE_RIGHT,
ST_SIDE_BOTTOM,
ST_SIDE_LEFT
} StSide;
typedef enum {
ST_CORNER_TOPLEFT,
ST_CORNER_TOPRIGHT,
ST_CORNER_BOTTOMRIGHT,
ST_CORNER_BOTTOMLEFT
} StCorner;
/* These are the CSS values; that doesn't mean we have to implement blink... */
typedef enum {
ST_TEXT_DECORATION_UNDERLINE = 1 << 0,
ST_TEXT_DECORATION_OVERLINE = 1 << 1,
ST_TEXT_DECORATION_LINE_THROUGH = 1 << 2,
ST_TEXT_DECORATION_BLINK = 1 << 3
} StTextDecoration;
GType st_theme_node_get_type (void) G_GNUC_CONST;
StThemeNode *st_theme_node_new (StThemeContext *context,
StThemeNode *parent_node, /* can be null */
StTheme *theme, /* can be null */
GType element_type,
const char *element_id,
const char *element_class,
const char *pseudo_class,
const char *inline_style);
StThemeNode *st_theme_node_get_parent (StThemeNode *node);
StTheme *st_theme_node_get_theme (StThemeNode *node);
GType st_theme_node_get_element_type (StThemeNode *node);
const char *st_theme_node_get_element_id (StThemeNode *node);
const char *st_theme_node_get_element_class (StThemeNode *node);
const char *st_theme_node_get_pseudo_class (StThemeNode *node);
/* Generic getters ... these are not cached so are less efficient. The other
* reason for adding the more specific version is that we can handle the
* details of the actual CSS rules, which can be complicated, especially
* for fonts
*/
gboolean st_theme_node_get_color (StThemeNode *node,
const char *property_name,
gboolean inherit,
ClutterColor *color);
gboolean st_theme_node_get_double (StThemeNode *node,
const char *property_name,
gboolean inherit,
double *value);
gboolean st_theme_node_get_length (StThemeNode *node,
const char *property_name,
gboolean inherit,
gdouble *length);
/* Specific getters for particular properties: cached
*/
void st_theme_node_get_background_color (StThemeNode *node,
ClutterColor *color);
void st_theme_node_get_foreground_color (StThemeNode *node,
ClutterColor *color);
const char *st_theme_node_get_background_image (StThemeNode *node);
double st_theme_node_get_border_width (StThemeNode *node,
StSide side);
double st_theme_node_get_border_radius (StThemeNode *node,
StCorner corner);
void st_theme_node_get_border_color (StThemeNode *node,
StSide side,
ClutterColor *color);
double st_theme_node_get_padding (StThemeNode *node,
StSide side);
StTextDecoration st_theme_node_get_text_decoration (StThemeNode *node);
/* Font rule processing is pretty complicated, so we just hardcode it
* under the standard font/font-family/font-size/etc names. This means
* you can't have multiple separate styled fonts for a single item,
* but that should be OK.
*/
const PangoFontDescription *st_theme_node_get_font (StThemeNode *node);
StBorderImage *st_theme_node_get_border_image (StThemeNode *node);
/* Helpers for get_preferred_width()/get_preferred_height() ClutterActor vfuncs */
void st_theme_node_adjust_for_height (StThemeNode *node,
float *for_height);
void st_theme_node_adjust_preferred_width (StThemeNode *node,
float *min_width_p,
float *natural_width_p);
void st_theme_node_adjust_for_width (StThemeNode *node,
float *for_width);
void st_theme_node_adjust_preferred_height (StThemeNode *node,
float *min_height_p,
float *natural_height_p);
/* Helper for allocate() ClutterActor vfunc */
void st_theme_node_get_content_box (StThemeNode *node,
const ClutterActorBox *actor_box,
ClutterActorBox *content_box);
gboolean st_theme_node_geometry_equal (StThemeNode *node,
StThemeNode *other);
G_END_DECLS
#endif /* __ST_THEME_NODE_H__ */

22
src/st/st-theme-private.h Normal file
View File

@ -0,0 +1,22 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __ST_THEME_PRIVATE_H__
#define __ST_THEME_PRIVATE_H__
#include <libcroco/libcroco.h>
#include "st-theme.h"
G_BEGIN_DECLS
GPtrArray *_st_theme_get_matched_properties (StTheme *theme,
StThemeNode *node);
/* Resolve an URL from the stylesheet to a filename */
char *_st_theme_resolve_url (StTheme *theme,
CRStyleSheet *base_stylesheet,
const char *url);
CRDeclaration *_st_theme_parse_declaration_list (const char *str);
G_END_DECLS
#endif /* __ST_THEME_PRIVATE_H__ */

1062
src/st/st-theme.c Normal file

File diff suppressed because it is too large Load Diff

38
src/st/st-theme.h Normal file
View File

@ -0,0 +1,38 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#ifndef __ST_THEME_H__
#define __ST_THEME_H__
#include <glib-object.h>
#include "st-theme-node.h"
G_BEGIN_DECLS
/**
* SECTION:StTheme
* @short_description: a set of stylesheets
*
* #StTheme holds a set of stylesheets. (The "cascade" of the name
* Cascading Stylesheets.) A #StTheme can be set to apply to all the actors
* in a stage using st_theme_context_set_theme() or applied to a subtree
* of actors using st_widget_set_theme().
*/
typedef struct _StThemeClass StThemeClass;
#define ST_TYPE_THEME (st_theme_get_type ())
#define ST_THEME(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), ST_TYPE_THEME, StTheme))
#define ST_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_THEME, StThemeClass))
#define ST_IS_THEME(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), ST_TYPE_THEME))
#define ST_IS_THEME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_THEME))
#define ST_THEME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_THEME, StThemeClass))
GType st_theme_get_type (void) G_GNUC_CONST;
StTheme *st_theme_new (const char *application_stylesheet,
const char *theme_stylesheet,
const char *default_stylesheet);
G_END_DECLS
#endif /* __ST_THEME_H__ */

665
src/st/st-tooltip.c Normal file
View File

@ -0,0 +1,665 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-tooltip.c: Plain tooltip actor
*
* Copyright 2008, 2009 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
/**
* SECTION:st-tooltip
* @short_description: A tooltip widget
*
* #StTooltip implements a single tooltip. It should not normally be created
* by the application but by the widget implementing tooltip capabilities, for
* example, #st_button_set_tooltip().
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <clutter/clutter.h>
#include "st-tooltip.h"
#include "st-widget.h"
#include "st-private.h"
enum
{
PROP_0,
PROP_LABEL,
PROP_TIP_AREA
};
#define ST_TOOLTIP_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TOOLTIP, StTooltipPrivate))
struct _StTooltipPrivate
{
ClutterActor *label;
gfloat arrow_offset;
gboolean actor_below;
ClutterGeometry *tip_area;
};
G_DEFINE_TYPE (StTooltip, st_tooltip, ST_TYPE_WIDGET);
static void
st_tooltip_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
StTooltip *tooltip = ST_TOOLTIP (gobject);
switch (prop_id)
{
case PROP_LABEL:
st_tooltip_set_label (tooltip, g_value_get_string (value));
break;
case PROP_TIP_AREA:
st_tooltip_set_tip_area (tooltip, g_value_get_boxed (value));
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_tooltip_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
StTooltipPrivate *priv = ST_TOOLTIP (gobject)->priv;
switch (prop_id)
{
case PROP_LABEL:
g_value_set_string (value, clutter_text_get_text (CLUTTER_TEXT (priv->label)));
break;
case PROP_TIP_AREA:
g_value_set_boxed (value, priv->tip_area);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
st_tooltip_style_changed (StWidget *self)
{
StTooltipPrivate *priv;
StThemeNode *theme_node;
ClutterColor color;
const PangoFontDescription *font;
gchar *font_string;
priv = ST_TOOLTIP (self)->priv;
theme_node = st_widget_get_theme_node (self);
st_theme_node_get_foreground_color (theme_node, &color);
clutter_text_set_color (CLUTTER_TEXT (priv->label), &color);
font = st_theme_node_get_font (theme_node);
font_string = pango_font_description_to_string (font);
clutter_text_set_font_name (CLUTTER_TEXT (priv->label), font_string);
g_free (font_string);
ST_WIDGET_CLASS (st_tooltip_parent_class)->style_changed (self);
}
static void
st_tooltip_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
gfloat min_label_w, natural_label_w;
gfloat label_height, arrow_height;
ClutterActor *arrow_image;
st_theme_node_adjust_for_height (theme_node, &for_height);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image)
{
clutter_actor_get_preferred_height (arrow_image,
-1,
NULL,
&arrow_height);
}
else
{
arrow_height = 0;
}
if (for_height > -1)
{
label_height = for_height - arrow_height;
}
else
{
label_height = -1;
}
if (priv->label)
{
clutter_actor_get_preferred_width (priv->label,
label_height,
&min_label_w,
&natural_label_w);
}
else
{
min_label_w = 0;
natural_label_w = 0;
}
st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
}
static void
st_tooltip_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
gfloat arrow_height;
gfloat min_label_h, natural_label_h;
ClutterActor *arrow_image;
st_theme_node_adjust_for_width (theme_node, &for_width);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image && !priv->actor_below)
{
clutter_actor_get_preferred_height (arrow_image,
-1,
NULL,
&arrow_height);
}
else
{
arrow_height = 0;
}
if (priv->label)
{
clutter_actor_get_preferred_height (priv->label,
for_width,
&min_label_h,
&natural_label_h);
}
else
{
min_label_h = 0;
natural_label_h = 0;
}
if (min_height_p)
*min_height_p = arrow_height + min_label_h;
if (natural_height_p)
*natural_height_p = arrow_height + natural_label_h;
st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
}
static void
st_tooltip_allocate (ClutterActor *self,
const ClutterActorBox *box,
ClutterAllocationFlags flags)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (self));
ClutterActorBox content_box, child_box, arrow_box;
gfloat arrow_height, arrow_width;
ClutterActor *border_image, *arrow_image;
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->allocate (self,
box,
flags);
st_theme_node_get_content_box (theme_node, box, &content_box);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image && !priv->actor_below)
{
clutter_actor_get_preferred_height (arrow_image, -1, NULL, &arrow_height);
clutter_actor_get_preferred_width (arrow_image, -1, NULL, &arrow_width);
arrow_box.x1 = (float)(priv->arrow_offset) - (int)(arrow_width / 2);
arrow_box.y1 = 0;
arrow_box.x2 = arrow_box.x1 + arrow_width;
arrow_box.y2 = arrow_box.y1 + arrow_height;
clutter_actor_allocate (arrow_image, &arrow_box, flags);
}
else
{
arrow_height = 0;
arrow_width = 0;
}
child_box.x1 = child_box.y1 = 0;
child_box.x2 = (box->x2 - box->x1);
child_box.y2 = (box->y2 - box->y1);
/* remove the space that is used by the arrow */
child_box.y1 += arrow_height;
border_image = st_widget_get_border_image (ST_WIDGET (self));
if (border_image)
clutter_actor_allocate (border_image, &child_box, flags);
if (priv->label)
{
child_box = content_box;
child_box.y1 += arrow_height;
clutter_actor_allocate (priv->label, &child_box, flags);
}
}
static void
st_tooltip_paint (ClutterActor *self)
{
ClutterActor *border_image, *arrow_image;
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
border_image = st_widget_get_border_image (ST_WIDGET (self));
if (border_image)
clutter_actor_paint (border_image);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image && !priv->actor_below)
clutter_actor_paint (arrow_image);
clutter_actor_paint (priv->label);
}
static void
st_tooltip_map (ClutterActor *self)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
ClutterActor *border_image, *arrow_image;
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->map (self);
border_image = st_widget_get_border_image (ST_WIDGET (self));
if (border_image)
clutter_actor_map (border_image);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image)
clutter_actor_map (arrow_image);
clutter_actor_map (priv->label);
}
static void
st_tooltip_unmap (ClutterActor *self)
{
StTooltipPrivate *priv = ST_TOOLTIP (self)->priv;
ClutterActor *border_image, *arrow_image;
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->unmap (self);
border_image = st_widget_get_border_image (ST_WIDGET (self));
if (border_image)
clutter_actor_unmap (border_image);
arrow_image = st_widget_get_background_image (ST_WIDGET (self));
if (arrow_image)
clutter_actor_unmap (arrow_image);
clutter_actor_unmap (priv->label);
}
static void
st_tooltip_class_init (StTooltipClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (StTooltipPrivate));
gobject_class->set_property = st_tooltip_set_property;
gobject_class->get_property = st_tooltip_get_property;
actor_class->get_preferred_width = st_tooltip_get_preferred_width;
actor_class->get_preferred_height = st_tooltip_get_preferred_height;
actor_class->allocate = st_tooltip_allocate;
actor_class->paint = st_tooltip_paint;
actor_class->map = st_tooltip_map;
actor_class->unmap = st_tooltip_unmap;
widget_class->style_changed = st_tooltip_style_changed;
pspec = g_param_spec_string ("label",
"Label",
"Label of the tooltip",
NULL, G_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
pspec = g_param_spec_boxed ("tip-area",
"Tip Area",
"Area on the stage the tooltip applies to",
CLUTTER_TYPE_GEOMETRY,
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TIP_AREA, pspec);
}
static void
st_tooltip_init (StTooltip *tooltip)
{
tooltip->priv = ST_TOOLTIP_GET_PRIVATE (tooltip);
tooltip->priv->label = g_object_new (CLUTTER_TYPE_TEXT,
"line-alignment", PANGO_ALIGN_CENTER,
"ellipsize", PANGO_ELLIPSIZE_END,
"use-markup", TRUE,
NULL);
tooltip->priv->tip_area = NULL;
clutter_actor_set_parent (CLUTTER_ACTOR (tooltip->priv->label),
CLUTTER_ACTOR (tooltip));
g_object_set (tooltip, "show-on-set-parent", FALSE, NULL);
clutter_actor_set_reactive (CLUTTER_ACTOR (tooltip), FALSE);
}
static void
st_tooltip_update_position (StTooltip *tooltip)
{
StTooltipPrivate *priv = tooltip->priv;
ClutterGeometry *tip_area = tooltip->priv->tip_area;
gfloat tooltip_w, tooltip_h, tooltip_x, tooltip_y;
gfloat stage_w, stage_h;
ClutterActor *stage;
/* ensure the tooltip with is not fixed size */
clutter_actor_set_size ((ClutterActor*) tooltip, -1, -1);
/* if no area set, just position ourselves top left */
if (!priv->tip_area)
{
clutter_actor_set_position ((ClutterActor*) tooltip, 0, 0);
return;
}
/* we need to have a style in case there are padding/border values to take into
* account when calculating width/height */
st_widget_ensure_style ((StWidget *) tooltip);
/* find out the tooltip's size */
clutter_actor_get_size ((ClutterActor*) tooltip, &tooltip_w, &tooltip_h);
/* attempt to place the tooltip */
tooltip_x = (int)(tip_area->x + (tip_area->width / 2) - (tooltip_w / 2));
tooltip_y = (int)(tip_area->y + tip_area->height);
stage = clutter_actor_get_stage ((ClutterActor *) tooltip);
if (!stage)
{
return;
}
clutter_actor_get_size (stage, &stage_w, &stage_h);
/* make sure the tooltip is not off screen vertically */
if (tooltip_w > stage_w)
{
tooltip_x = 0;
clutter_actor_set_width ((ClutterActor*) tooltip, stage_w);
}
else if (tooltip_x < 0)
{
tooltip_x = 0;
}
else if (tooltip_x + tooltip_w > stage_w)
{
tooltip_x = (int)(stage_w) - tooltip_w;
}
/* make sure the tooltip is not off screen horizontally */
if (tooltip_y + tooltip_h > stage_h)
{
priv->actor_below = TRUE;
/* re-query size as may have changed */
clutter_actor_get_preferred_height ((ClutterActor*) tooltip,
-1, NULL, &tooltip_h);
tooltip_y = tip_area->y - tooltip_h;
}
else
{
priv->actor_below = FALSE;
}
/* calculate the arrow offset */
priv->arrow_offset = tip_area->x + tip_area->width / 2 - tooltip_x;
clutter_actor_set_position ((ClutterActor*) tooltip, tooltip_x, tooltip_y);
}
/**
* st_tooltip_get_label:
* @tooltip: a #StTooltip
*
* Get the text displayed on the tooltip
*
* Returns: the text for the tooltip. This must not be freed by the application
*/
G_CONST_RETURN gchar *
st_tooltip_get_label (StTooltip *tooltip)
{
g_return_val_if_fail (ST_IS_TOOLTIP (tooltip), NULL);
return clutter_text_get_text (CLUTTER_TEXT (tooltip->priv->label));
}
/**
* st_tooltip_set_label:
* @tooltip: a #StTooltip
* @text: text to set the label to
*
* Sets the text displayed on the tooltip
*/
void
st_tooltip_set_label (StTooltip *tooltip,
const gchar *text)
{
StTooltipPrivate *priv;
g_return_if_fail (ST_IS_TOOLTIP (tooltip));
priv = tooltip->priv;
clutter_text_set_text (CLUTTER_TEXT (priv->label), text);
g_object_notify (G_OBJECT (tooltip), "label");
}
/**
* st_tooltip_show:
* @tooltip: a #StTooltip
*
* Show the tooltip relative to the associated widget.
*/
void
st_tooltip_show (StTooltip *tooltip)
{
StTooltipPrivate *priv;
ClutterActor *parent;
ClutterActor *stage;
ClutterActor *self = CLUTTER_ACTOR (tooltip);
ClutterAnimation *animation;
/* make sure we're not currently already animating (e.g. hiding) */
animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
if (animation)
clutter_animation_completed (animation);
priv = tooltip->priv;
parent = clutter_actor_get_parent (self);
stage = clutter_actor_get_stage (self);
if (!stage)
{
g_warning ("StTooltip is not on any stage.");
return;
}
/* make sure we're parented on the stage */
if (G_UNLIKELY (parent != stage))
{
g_object_ref (self);
clutter_actor_unparent (self);
clutter_actor_set_parent (self, stage);
g_object_unref (self);
parent = stage;
}
/* raise the tooltip to the top */
clutter_container_raise_child (CLUTTER_CONTAINER (stage),
CLUTTER_ACTOR (tooltip),
NULL);
st_tooltip_update_position (tooltip);
/* finally show the tooltip... */
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->show (self);
/* and give it some bounce! */
g_object_set (G_OBJECT (self),
"scale-center-x", priv->arrow_offset,
"scale-center-y", (priv->actor_below) ? clutter_actor_get_height (self) : 0,
NULL);
clutter_actor_set_scale (self, 0.0, 0.0);
clutter_actor_animate (self, CLUTTER_EASE_OUT_ELASTIC,
500,
"scale-x", 1.0,
"scale-y", 1.0,
NULL);
}
static void
st_tooltip_hide_complete (ClutterAnimation *animation,
ClutterActor *actor)
{
CLUTTER_ACTOR_CLASS (st_tooltip_parent_class)->hide (actor);
g_signal_handlers_disconnect_by_func (actor,
st_tooltip_hide_complete,
actor);
}
/**
* st_tooltip_hide:
* @tooltip: a #StTooltip
*
* Hide the tooltip
*/
void
st_tooltip_hide (StTooltip *tooltip)
{
ClutterAnimation *animation;
g_return_if_fail (ST_TOOLTIP (tooltip));
/* make sure we're not currently already animating (e.g. hiding) */
animation = clutter_actor_get_animation (CLUTTER_ACTOR (tooltip));
if (animation)
clutter_animation_completed (animation);
g_object_set (G_OBJECT (tooltip),
"scale-center-x", tooltip->priv->arrow_offset,
NULL);
animation =
clutter_actor_animate (CLUTTER_ACTOR (tooltip), CLUTTER_EASE_IN_SINE,
150,
"scale-x", 0.0,
"scale-y", 0.0,
NULL);
g_signal_connect (animation, "completed",
G_CALLBACK (st_tooltip_hide_complete), tooltip);
}
/**
* st_tooltip_set_tip_area:
* @tooltip: A #StTooltip
* @area: A #ClutterGeometry
*
* Set the area on the stage that the tooltip applies to.
*/
void
st_tooltip_set_tip_area (StTooltip *tooltip,
const ClutterGeometry *area)
{
g_return_if_fail (ST_IS_TOOLTIP (tooltip));
if (tooltip->priv->tip_area)
g_boxed_free (CLUTTER_TYPE_GEOMETRY, tooltip->priv->tip_area);
tooltip->priv->tip_area = g_boxed_copy (CLUTTER_TYPE_GEOMETRY, area);
st_tooltip_update_position (tooltip);
}
/**
* st_tooltip_get_tip_area:
* @tooltip: A #StTooltip
*
* Retrieve the area on the stage that the tooltip currently applies to
*
* Returns: the #ClutterGeometry, owned by the tooltip which must not be freed
* by the application.
*/
G_CONST_RETURN ClutterGeometry*
st_tooltip_get_tip_area (StTooltip *tooltip)
{
g_return_val_if_fail (ST_IS_TOOLTIP (tooltip), NULL);
return tooltip->priv->tip_area;
}

81
src/st/st-tooltip.h Normal file
View File

@ -0,0 +1,81 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-tooltip.h: Plain tooltip actor
*
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Written by: Thomas Wood <thomas@linux.intel.com>
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TOOLTIP_H__
#define __ST_TOOLTIP_H__
G_BEGIN_DECLS
#include <st/st-bin.h>
#define ST_TYPE_TOOLTIP (st_tooltip_get_type ())
#define ST_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TOOLTIP, StTooltip))
#define ST_IS_TOOLTIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TOOLTIP))
#define ST_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TOOLTIP, StTooltipClass))
#define ST_IS_TOOLTIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TOOLTIP))
#define ST_TOOLTIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TOOLTIP, StTooltipClass))
typedef struct _StTooltip StTooltip;
typedef struct _StTooltipPrivate StTooltipPrivate;
typedef struct _StTooltipClass StTooltipClass;
/**
* StTooltip:
*
* The contents of this structure is private and should only be accessed using
* the provided API.
*/
struct _StTooltip
{
/*< private >*/
StBin parent_instance;
StTooltipPrivate *priv;
};
struct _StTooltipClass
{
StBinClass parent_class;
};
GType st_tooltip_get_type (void) G_GNUC_CONST;
G_CONST_RETURN gchar *st_tooltip_get_label (StTooltip *tooltip);
void st_tooltip_set_label (StTooltip *tooltip,
const gchar *text);
void st_tooltip_show (StTooltip *tooltip);
void st_tooltip_hide (StTooltip *tooltip);
void st_tooltip_set_tip_area (StTooltip *tooltip,
const ClutterGeometry *area);
G_CONST_RETURN ClutterGeometry* st_tooltip_get_tip_area (StTooltip *tooltip);
G_END_DECLS
#endif /* __ST_TOOLTIP_H__ */

48
src/st/st-types.h Normal file
View File

@ -0,0 +1,48 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
/**
* SECTION:st-types
* @short_description: type definitions used throughout St
*
* Common types for StWidgets.
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_TYPES_H__
#define __ST_TYPES_H__
#include <glib-object.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
typedef enum {
ST_ALIGN_START,
ST_ALIGN_MIDDLE,
ST_ALIGN_END
} StAlign;
G_END_DECLS
#endif /* __ST_TYPES_H__ */

1395
src/st/st-widget.c Normal file

File diff suppressed because it is too large Load Diff

117
src/st/st-widget.h Normal file
View File

@ -0,0 +1,117 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* st-widget.h: Base class for St actors
*
* Copyright 2007 OpenedHand
* Copyright 2008, 2009 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
* Boston, MA 02111-1307, USA.
*
*/
#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
#error "Only <st/st.h> can be included directly.h"
#endif
#ifndef __ST_WIDGET_H__
#define __ST_WIDGET_H__
#include <clutter/clutter.h>
#include <st/st-types.h>
#include <st/st-theme.h>
#include <st/st-theme-node.h>
G_BEGIN_DECLS
#define ST_TYPE_WIDGET (st_widget_get_type ())
#define ST_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_WIDGET, StWidget))
#define ST_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_WIDGET))
#define ST_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_WIDGET, StWidgetClass))
#define ST_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_WIDGET))
#define ST_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_WIDGET, StWidgetClass))
typedef struct _StWidget StWidget;
typedef struct _StWidgetPrivate StWidgetPrivate;
typedef struct _StWidgetClass StWidgetClass;
/**
* StWidget:
*
* Base class for stylable actors. The contents of the #StWidget
* structure are private and should only be accessed through the
* public API.
*/
struct _StWidget
{
/*< private >*/
ClutterActor parent_instance;
StWidgetPrivate *priv;
};
/**
* StWidgetClass:
*
* Base class for stylable actors.
*/
struct _StWidgetClass
{
/*< private >*/
ClutterActorClass parent_class;
/* vfuncs */
void (* draw_background) (StWidget *self);
void (* style_changed) (StWidget *self);
};
GType st_widget_get_type (void) G_GNUC_CONST;
void st_widget_set_style_pseudo_class (StWidget *actor,
const gchar *pseudo_class);
G_CONST_RETURN gchar *st_widget_get_style_pseudo_class (StWidget *actor);
void st_widget_set_style_class_name (StWidget *actor,
const gchar *style_class);
G_CONST_RETURN gchar *st_widget_get_style_class_name (StWidget *actor);
void st_widget_set_style (StWidget *actor,
const gchar *style);
G_CONST_RETURN gchar *st_widget_get_style (StWidget *actor);
void st_widget_set_theme (StWidget *actor,
StTheme *theme);
StTheme * st_widget_get_theme (StWidget *actor);
void st_widget_set_has_tooltip (StWidget *widget,
gboolean has_tooltip);
gboolean st_widget_get_has_tooltip (StWidget *widget);
void st_widget_set_tooltip_text (StWidget *widget,
const gchar *text);
const gchar* st_widget_get_tooltip_text (StWidget *widget);
void st_widget_show_tooltip (StWidget *widget);
void st_widget_hide_tooltip (StWidget *widget);
void st_widget_ensure_style (StWidget *widget);
/* Only to be used by sub-classes of StWidget */
void st_widget_style_changed (StWidget *widget);
StThemeNode *st_widget_get_theme_node (StWidget *widget);
ClutterActor *st_widget_get_background_image (StWidget *actor);
ClutterActor *st_widget_get_border_image (StWidget *actor);
void st_widget_draw_background (StWidget *widget);
G_END_DECLS
#endif /* __ST_WIDGET_H__ */

392
src/st/test-theme.c Normal file
View File

@ -0,0 +1,392 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include <clutter/clutter.h>
#include "st-theme.h"
#include "st-theme-context.h"
#include <math.h>
#include <string.h>
static StThemeNode *root;
static StThemeNode *group1;
static StThemeNode *text1;
static StThemeNode *text2;
static StThemeNode *group2;
static StThemeNode *text3;
static StThemeNode *text4;
static StThemeNode *group3;
static StThemeNode *cairo_texture;
static gboolean fail;
static const char *test;
static void
assert_font (StThemeNode *node,
const char *node_description,
const char *expected)
{
char *value = pango_font_description_to_string (st_theme_node_get_font (node));
if (strcmp (expected, value) != 0)
{
g_print ("%s: %s.font: expected: %s, got: %s\n",
test, node_description, expected, value);
fail = TRUE;
}
g_free (value);
}
static char *
text_decoration_to_string (StTextDecoration decoration)
{
GString *result = g_string_new (NULL);
if (decoration & ST_TEXT_DECORATION_UNDERLINE)
g_string_append(result, " underline");
if (decoration & ST_TEXT_DECORATION_OVERLINE)
g_string_append(result, " overline");
if (decoration & ST_TEXT_DECORATION_LINE_THROUGH)
g_string_append(result, " line_through");
if (decoration & ST_TEXT_DECORATION_BLINK)
g_string_append(result, " blink");
if (result->len > 0)
g_string_erase (result, 0, 1);
else
g_string_append(result, "none");
return g_string_free (result, FALSE);
}
static void
assert_text_decoration (StThemeNode *node,
const char *node_description,
StTextDecoration expected)
{
StTextDecoration value = st_theme_node_get_text_decoration (node);
if (expected != value)
{
char *es = text_decoration_to_string (expected);
char *vs = text_decoration_to_string (value);
g_print ("%s: %s.text-decoration: expected: %s, got: %s\n",
test, node_description, es, vs);
fail = TRUE;
g_free (es);
g_free (vs);
}
}
static void
assert_foreground_color (StThemeNode *node,
const char *node_description,
guint32 expected)
{
ClutterColor color;
st_theme_node_get_foreground_color (node, &color);
guint32 value = clutter_color_to_pixel (&color);
if (expected != value)
{
g_print ("%s: %s.color: expected: #%08x, got: #%08x\n",
test, node_description, expected, value);
fail = TRUE;
}
}
static void
assert_background_color (StThemeNode *node,
const char *node_description,
guint32 expected)
{
ClutterColor color;
st_theme_node_get_background_color (node, &color);
guint32 value = clutter_color_to_pixel (&color);
if (expected != value)
{
g_print ("%s: %s.background-color: expected: #%08x, got: #%08x\n",
test, node_description, expected, value);
fail = TRUE;
}
}
static const char *
side_to_string (StSide side)
{
switch (side)
{
case ST_SIDE_TOP:
return "top";
case ST_SIDE_RIGHT:
return "right";
case ST_SIDE_BOTTOM:
return "bottom";
case ST_SIDE_LEFT:
return "left";
}
return "<unknown>";
}
static void
assert_border_color (StThemeNode *node,
const char *node_description,
StSide side,
guint32 expected)
{
ClutterColor color;
st_theme_node_get_border_color (node, side, &color);
guint32 value = clutter_color_to_pixel (&color);
if (expected != value)
{
g_print ("%s: %s.border-%s-color: expected: #%08x, got: #%08x\n",
test, node_description, side_to_string (side), expected, value);
fail = TRUE;
}
}
static void
assert_background_image (StThemeNode *node,
const char *node_description,
const char *expected)
{
const char *value = st_theme_node_get_background_image (node);
if (expected == NULL)
expected = "(null)";
if (value == NULL)
value = "(null)";
if (strcmp (expected, value) != 0)
{
g_print ("%s: %s.background-image: expected: %s, got: %s\n",
test, node_description, expected, value);
fail = TRUE;
}
}
#define LENGTH_EPSILON 0.001
static void
assert_length (const char *node_description,
const char *property_description,
double expected,
double value)
{
if (fabs (expected - value) > LENGTH_EPSILON)
{
g_print ("%s %s.%s: expected: %3f, got: %3f\n",
test, node_description, property_description, expected, value);
fail = TRUE;
}
}
static void
test_defaults (void)
{
test = "defaults";
/* font comes from context */
assert_font (root, "stage", "sans-serif 12");
/* black is the default foreground color */
assert_foreground_color (root, "stage", 0x00000ff);
}
static void
test_lengths (void)
{
test = "lengths";
/* 12pt == 16px at 96dpi */
assert_length ("group1", "padding-top", 16.,
st_theme_node_get_padding (group1, ST_SIDE_TOP));
/* 12px == 12px */
assert_length ("group1", "padding-right", 12.,
st_theme_node_get_padding (group1, ST_SIDE_RIGHT));
/* 2em == 32px (with a 12pt font) */
assert_length ("group1", "padding-bottom", 32.,
st_theme_node_get_padding (group1, ST_SIDE_BOTTOM));
/* 1in == 72pt == 96px, at 96dpi */
assert_length ("group1", "padding-left", 96.,
st_theme_node_get_padding (group1, ST_SIDE_LEFT));
}
static void
test_classes (void)
{
test = "classes";
/* .special-text class overrides size and style;
* the ClutterTexture.special-text selector doesn't match */
assert_font (text1, "text1", "sans-serif Italic 32px");
}
static void
test_type_inheritance (void)
{
test = "type_inheritance";
/* From ClutterTexture element selector */
assert_length ("cairoTexture", "padding-top", 10.,
st_theme_node_get_padding (cairo_texture, ST_SIDE_TOP));
/* From ClutterCairoTexture element selector */
assert_length ("cairoTexture", "padding-right", 20.,
st_theme_node_get_padding (cairo_texture, ST_SIDE_RIGHT));
}
static void
test_adjacent_selector (void)
{
test = "adjacent_selector";
/* #group1 > #text1 matches text1 */
assert_foreground_color (text1, "text1", 0x00ff00ff);
/* stage > #text2 doesn't match text2 */
assert_foreground_color (text2, "text2", 0x000000ff);
}
static void
test_padding (void)
{
test = "padding";
/* Test that a 4-sided padding property assigns the right paddings to
* all sides */
assert_length ("group2", "padding-top", 1.,
st_theme_node_get_padding (group2, ST_SIDE_TOP));
assert_length ("group2", "padding-right", 2.,
st_theme_node_get_padding (group2, ST_SIDE_RIGHT));
assert_length ("group2", "padding-bottom", 3.,
st_theme_node_get_padding (group2, ST_SIDE_BOTTOM));
assert_length ("group2", "padding-left", 4.,
st_theme_node_get_padding (group2, ST_SIDE_LEFT));
}
static void
test_border (void)
{
test = "border";
/* group2 is defined as having a thin black border along the top three
* sides with rounded joins, then a square-joined green border at the
* botttom
*/
assert_length ("group2", "border-top-width", 2.,
st_theme_node_get_border_width (group2, ST_SIDE_TOP));
assert_length ("group2", "border-right-width", 2.,
st_theme_node_get_border_width (group2, ST_SIDE_RIGHT));
assert_length ("group2", "border-bottom-width", 5.,
st_theme_node_get_border_width (group2, ST_SIDE_BOTTOM));
assert_length ("group2", "border-left-width", 2.,
st_theme_node_get_border_width (group2, ST_SIDE_LEFT));
assert_border_color (group2, "group2", ST_SIDE_TOP, 0x000000ff);
assert_border_color (group2, "group2", ST_SIDE_RIGHT, 0x000000ff);
assert_border_color (group2, "group2", ST_SIDE_BOTTOM, 0x0000ffff);
assert_border_color (group2, "group2", ST_SIDE_LEFT, 0x000000ff);
assert_length ("group2", "border-radius-topleft", 10.,
st_theme_node_get_border_radius (group2, ST_CORNER_TOPLEFT));
assert_length ("group2", "border-radius-topright", 10.,
st_theme_node_get_border_radius (group2, ST_CORNER_TOPRIGHT));
assert_length ("group2", "border-radius-bottomright", 0.,
st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMRIGHT));
assert_length ("group2", "border-radius-bottomleft", 0.,
st_theme_node_get_border_radius (group2, ST_CORNER_BOTTOMLEFT));
}
static void
test_background (void)
{
test = "background";
/* group1 has a background: shortcut property setting color and image */
assert_background_color (group1, "group1", 0xff0000ff);
assert_background_image (group1, "group1", "st/some-background.png");
/* text1 inherits the background image but not the color */
assert_background_color (text1, "text1", 0x00000000);
assert_background_image (text1, "text1", "st/some-background.png");
/* text1 inherits inherits both, but then background: none overrides both */
assert_background_color (text2, "text2", 0x00000000);
assert_background_image (text2, "text2", NULL);
/* background-image property */
assert_background_image (group2, "group2", "st/other-background.png");
}
static void
test_font (void)
{
test = "font";
/* font specified with font: */
assert_font (group2, "group2", "serif Italic 12px");
/* text3 inherits and overrides individually properties */
assert_font (text3, "text3", "serif Bold Oblique Small-Caps 24px");
}
static void
test_pseudo_class (void)
{
test = "pseudo_class";
/* text4 has :visited and :hover pseudo-classes, so should pick up both of these */
assert_foreground_color (text4, "text4", 0x888888ff);
assert_text_decoration (text4, "text4", ST_TEXT_DECORATION_UNDERLINE);
/* :hover pseudo-class matches, but class doesn't match */
assert_text_decoration (group3, "group3", 0);
}
static void
test_inline_style (void)
{
test = "inline_style";
/* These properties come from the inline-style specified when creating the node */
assert_foreground_color (text3, "text3", 0x00000ffff);
assert_length ("text3", "padding-bottom", 12.,
st_theme_node_get_padding (text3, ST_SIDE_BOTTOM));
}
int
main (int argc, char **argv)
{
StTheme *theme;
StThemeContext *context;
clutter_init (&argc, &argv);
theme = st_theme_new ("st/test-theme.css",
NULL, NULL);
context = st_theme_context_new ();
st_theme_context_set_theme (context, theme);
st_theme_context_set_resolution (context, 96.);
st_theme_context_set_font (context,
pango_font_description_from_string ("sans-serif 12"));
root = st_theme_context_get_root_node (context);
group1 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_GROUP, "group1", NULL, NULL, NULL);
text1 = st_theme_node_new (context, group1, NULL,
CLUTTER_TYPE_TEXT, "text1", "special-text", NULL, NULL);
text2 = st_theme_node_new (context, group1, NULL,
CLUTTER_TYPE_TEXT, "text2", NULL, NULL, NULL);
group2 = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_GROUP, "group2", NULL, NULL, NULL);
text3 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_TEXT, "text3", NULL, NULL,
"color: #0000ff; padding-bottom: 12px;");
text4 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_TEXT, "text4", NULL, "visited hover", NULL);
group3 = st_theme_node_new (context, group2, NULL,
CLUTTER_TYPE_GROUP, "group3", NULL, "hover", NULL);
cairo_texture = st_theme_node_new (context, root, NULL,
CLUTTER_TYPE_CAIRO_TEXTURE, "cairoTexture", NULL, NULL, NULL);
test_defaults ();
test_lengths ();
test_classes ();
test_type_inheritance ();
test_adjacent_selector ();
test_padding ();
test_border ();
test_background ();
test_font ();
test_pseudo_class ();
test_inline_style ();
return fail ? 1 : 0;
}

Some files were not shown because too many files have changed in this diff Show More