Compare commits

...

48 Commits

Author SHA1 Message Date
Colin Walters
e191636f34 Update for review of e57b7ec335 2009-06-30 15:29:10 -04:00
Colin Walters
39e31a3aa9 Update for comments on b5988a57fa 2009-06-30 15:29:10 -04:00
Colin Walters
8293f3423a Update for review of d5a80d3063 2009-06-30 15:29:10 -04:00
Marina Zhurakhinskaya
858a82fcdd Use a separate icon image as a drag actor instead of using the clone of the icon
Clutter no longer allows using a clone of an actor that is not a part of
the scene graph. This is what used to happen when we created a clone for
the icon of the item that was being dragged, and then closed the More panes
with the original item, removing the icon from the scene graph. This was
also when happened when the user hit Esc while dragging, which prompted the
overlay to close, removing the original icon from the scene graph.

Rename getIcon() methods to createIcon() to better reflect on the fact that
a new icon is created each time the method is called (we do use cache in
some cases).

Remove a stray log message in overlay.js

Fixes http://bugzilla.gnome.org/show_bug.cgi?id=585490
and http://bugzilla.gnome.org/show_bug.cgi?id=585489
2009-06-29 15:08:48 -04:00
Marina Zhurakhinskaya
2ae89f3b36 Merge branch 'master' into my-overlay-design 2009-06-29 14:40:04 -04:00
Marina Zhurakhinskaya
cdee026095 Make workspaces accept a drop of AppDisplay.WellDisplayItem
AppDispplay.WellDisplayItem needed to be added along with the
GenericDisplay.GenericDisplayItem as a type of a drop object
that workspaces accept.
2009-06-29 12:28:21 -04:00
Colin Walters
340fbfe943 Avoid duplicating most used applications in the AppDisplay cache
All used applications should be in the database from the menus
anyways.
2009-06-26 18:35:48 -04:00
Colin Walters
c577951ec9 Fix getMostUsedApps to avoid returning duplicate items 2009-06-26 18:30:28 -04:00
Colin Walters
d588b083d9 Fix invalid function signature and a memory leak
Add the missing GParamSpec to on_n_workspaces_changed.

Also, we don't need to re-dup the appid, since it's already dup'd.
2009-06-26 17:12:55 -04:00
Colin Walters
e57b7ec335 Replace main AppDisplay with AppWell
This is a start towards implementing the 02 overlay design.  The
default applications has moved into GConf.  We keep around an AppDisplay
instance for handling the right side behavior.
2009-06-25 20:32:08 -04:00
Colin Walters
b5988a57fa ShellAppSystem: Add API for looking up and manipulating favorites
The ShellAppSystem now proxies the GConf favorites key.
2009-06-25 20:32:08 -04:00
Colin Walters
d5a80d3063 ShellAppMonitor now works harder to assocate windows with desktop files
Instead of just keeping a heuristic wmclass match, call into the app
system to more strongly associate windows with desktop file IDs.
2009-06-25 20:32:08 -04:00
Marina Zhurakhinskaya
02a8cd5ce2 Use a single ItemResults class instead of AppResults and DocResults
AppResults and DocResults classes were identical with an exception of
the display class they used and the text label for the results. Merged
them into a single ItemResults class that takes these two additional
arguments.
2009-06-25 19:14:29 -04:00
Marina Zhurakhinskaya
aa77762d27 Move the activate and select functionality inside the callbacks
Move the activate and select functionality inside the callbacks for
'button-release-event' signals of the display item and the information
button correspondingly. This way it is more obvious that this is an
event handling code that needs to return a boolean value for whether
the signal has been fully handled by the actor.
2009-06-25 18:01:45 -04:00
Marina Zhurakhinskaya
de3f5dec68 Use TextureCache to load the information icon
Use TextureCache to load the information icon, so that we don't create a
new ClutterTexture for information icons corresponding to each display
item.
2009-06-25 17:24:46 -04:00
Marina Zhurakhinskaya
84865f416d Make sure we show the information button when a new item appears under the pointer
Update the code for checking a display item under the pointer to expect
the item itself rather than its child to be returned by stage_get_actor_at_pos().

This code is now used to display an information button when an item is
drawn under the pointer, so update the comment accordingly.
2009-06-25 15:31:09 -04:00
Marina Zhurakhinskaya
ca3e3df199 Show and hide dash panes instead of adding and removing each time
Add results and details panes up-front, and show and hide them instead
of adding and removing them each time
2009-06-25 14:54:28 -04:00
Marina Zhurakhinskaya
643febebf8 Add a comment about the use of the transparent background and set its opacity to 0
Add a comment about the use of the transparent background to catch clicks
in the workspaces area when the dash panes are being displayed and dismiss
the dash panes.

Set opacity for the background to 0 instead of using a transparent background
color so that Clutter optimizes the drawing of the background actor.
2009-06-25 14:33:24 -04:00
Marina Zhurakhinskaya
6d002c893d Fix up horizontal gradient code and its use
Fix up the comments about the horizontal gradient code and use 8x1 texture
instead of 8x8.

Make sure the values we assign to the three-stop horizontal gradient
require the use of the three stop gradient, with the middle value not being
right between the side values.
2009-06-25 14:32:00 -04:00
Marina Zhurakhinskaya
5eaed34047 Display search results automatically
Display a pane with search results for both applications and documents
automatically when a search string is entered.

Allow viewing search results for the individual section when More link
for applications or documents is clicked.

Move text labels for the applications and documents sections into the
respective classes.
2009-06-24 18:24:48 -04:00
Marina Zhurakhinskaya
8f5b55350f Add back search functionality
Enable typing in the search box and display results in the results pane.
This means that the user has to open the details pane for applications
or documents to view the results for now.

Connect Enter to launch the seleted item.

Connect Escape to clear search, remove results and details panes,
or exit overlay.
2009-06-22 19:06:50 -04:00
Marina Zhurakhinskaya
c600ebb687 Merge branch 'master' into my-overlay-design 2009-06-22 16:14:55 -04:00
Siegfried-Angel Gevatter Pujals
9abc062a64 Hide details panel by default
Don't show the details panel when the overlay is activated,
only when explicitly requested by clicking the info icon.
2009-06-22 16:10:39 -04:00
Marina Zhurakhinskaya
81d0474926 Fix the height allocated to the results sections
The results sections no longer include a label on top of them, so the
height of that label needs to be subtracted when specifying the height
for the sections. This ensures that display controls are positioned
correctly on the bottom of the section.
2009-06-18 20:01:49 -04:00
Marina Zhurakhinskaya
c41902c188 Select an item when information button is clicked, launch on single click
Clicking the information button for an item selects it (i.e. highlights it)
and shows details about the item.
Clicking the rest of the item area launches it.
Item does not become draggable if the dragging is started over the information
icon (i.e. if the user presses the information icon, but releases elsewhere).

Make sure we emit "activated" signal and close the overlay when an item from
one of the results displays is launched.
2009-06-18 19:53:21 -04:00
Marina Zhurakhinskaya
97b9ccbff7 Add an icon for the information link
Use an (i) icon supplied by Jeremy for the information link and display
it on hover. Make sure it is positioned nicely and the text doesn't
overlap with it.
2009-06-18 17:50:56 -04:00
Colin Walters
282daf768f Merge branch 'master' into overlay-design02 2009-06-18 17:26:03 -04:00
Marina Zhurakhinskaya
1b057300b0 Remove pop-up previews
Pop-up previews are not part of the new design and interfere with the information link.

Make sure details display for applications has the appropriate width set.
2009-06-18 17:12:58 -04:00
Colin Walters
75da772d05 Merge branch 'master' into overlay-design02
Conflicts:
	js/ui/appDisplay.js
2009-06-18 13:12:21 -04:00
Marina Zhurakhinskaya
df9cf98826 Make sure at most one item is selected in the overlay
Make sure at most one item is selected in the overlay and we always show
a details pane for the selected item.

Improve the positioning of the search box.

Remove a duplicate variable DASH_PAD and use DASH_SECTION_PADDING everywhere instead.
2009-06-17 18:12:02 -04:00
Colin Walters
d3cb8e5b21 Split out separate AppResults, DocResults classes. Readd search as stub.
Avoid bloating the Dash class by separating out a bit more functionality.
2009-06-17 17:38:08 -04:00
Marina Zhurakhinskaya
71998a6b43 Merge branch 'master' into my-overlay-design 2009-06-17 13:54:39 -04:00
Colin Walters
2fd2293e4a Merge branch 'master' into overlay-design02 2009-06-17 10:12:31 -04:00
Marina Zhurakhinskaya
3528eef655 Change selected item color and make sure only one item is selected at a time
A blue selected item color fits better with the new color scheme than a green one.

Only one item should be selected across all the displays we are showing.
2009-06-16 16:30:42 -04:00
Marina Zhurakhinskaya
f209d6792d Display a pane with item details
Display a pane with item details, such as a full image previews, when an item is single clicked.

Add a placeholder information link that shows up when an item is moused over.
2009-06-16 14:50:38 -04:00
Marina Zhurakhinskaya
614e83476e Merge branch 'master' into my-overlay-design 2009-06-16 13:04:56 -04:00
Marina Zhurakhinskaya
4e2a301ef0 Add icons from Jeremy
These icons are for the "more", "close", and "info" images in the overlay.
2009-06-11 19:00:12 -04:00
Marina Zhurakhinskaya
6def8cf7dd Merge branch 'master' into my-overlay-design 2009-06-11 18:24:42 -04:00
Marina Zhurakhinskaya
1204898d1e Display all recent documents in the results pane
Display all recent documents in the results pane, in addition to the first
few displayed in the main dash. All documents can be viewed with the help
of a paging control.
2009-06-09 16:11:51 -04:00
Marina Zhurakhinskaya
98530afd87 Add a results pane in the overlay
Display the results pane above the workspaces. The results pane is somewhat
transparent and has a blue gradient background. The dash pane is slightly
transparent and also has a blue gradient background.

The results pane shows up when a More control is clicked. It disappears when
a Less control is clicked, an area outside of the dash area is clicked,
an item starts being dragged, or the overlay mode is exited.

Add shell_global_create_horizontal_gradient() to shell-global.[ch]
2009-06-08 19:06:23 -04:00
Marina Zhurakhinskaya
a9fedcce76 Merge branch 'master' into my-overlay-design 2009-06-08 18:02:31 -04:00
Colin Walters
dae1d94258 Merge branch 'master' into overlay-design02 2009-06-04 17:43:48 -04:00
Colin Walters
4a7c328c33 Revert inadvertent commits for future rebasing 2009-06-04 17:42:03 -04:00
Colin Walters
2c0d6fdf89 favorites 2009-06-03 11:49:42 -04:00
Colin Walters
12f896eb94 random hack 2009-06-02 18:18:25 -04:00
Colin Walters
230c91b6d4 Functionality in ShellAppMonitor for tracking active applications
Hook into the metacity core for notification of new/removed windows,
and use the WM_CLASS data to map these to applications.  This is
a first pass; we need further redudancy in the system.
2009-06-02 18:18:25 -04:00
Colin Walters
d7dcfe6a06 Make ShellAppMonitor and ShellAppSystem singletons 2009-06-02 17:21:50 -04:00
Colin Walters
275eba17db Remove a lot of obsolete code from old Sideshow (now Dash) 2009-06-02 13:13:51 -04:00
19 changed files with 1675 additions and 798 deletions

74
data/close.svg Normal file
View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Foreground"
x="0px"
y="0px"
width="16px"
height="16px"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="x_circle_16.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata2399"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs2397"><linearGradient
id="linearGradient3173"><stop
style="stop-color:#c4c4c4;stop-opacity:1;"
offset="0"
id="stop3175" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3177" /></linearGradient><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 8 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="16 : 8 : 1"
inkscape:persp3d-origin="8 : 5.3333333 : 1"
id="perspective2401" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3173"
id="linearGradient3179"
x1="7.844358"
y1="16"
x2="7.7198443"
y2="-0.062256809"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
inkscape:window-height="713"
inkscape:window-width="1197"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
showgrid="false"
inkscape:zoom="32.125"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-x="40"
inkscape:window-y="40"
inkscape:current-layer="Foreground" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M10.5,3.5l2,2L10,8l2.5,2.5l-2,2L8,10l-2.5,2.5l-2-2L6,8L3.5,5.5l2-2L8,6L10.5,3.5 z M0,8c0-4.418,3.582-8,8-8s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
id="path2394"
style="fill-opacity:1;fill:url(#linearGradient3179)" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -15,6 +15,21 @@
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/favorite_apps</key>
<applyto>/desktop/gnome/shell/favorite_apps</applyto>
<owner>gnome-shell</owner>
<type>list</type>
<listtype>string</listtype>
<default>[mozilla-firefox.desktop,evolution.desktop,openoffice.org-writer.desktop]</default>
<locale name="C">
<short>List of desktop file IDs for favorite applications</short>
<long>
The applications corresponding to these identifiers will be displayed in the favorites area.
</long>
</locale>
</schema>
</schemalist>
</gconfschemafile>

74
data/info.svg Normal file
View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Foreground"
x="0px"
y="0px"
width="16px"
height="16px"
viewBox="0 0 16 16"
enable-background="new 0 0 16 16"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="info_16.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata2389"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs2387"><linearGradient
id="linearGradient3710"><stop
style="stop-color:#c4c4c4;stop-opacity:1;"
offset="0"
id="stop3712" /><stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop3714" /></linearGradient><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 8 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="16 : 8 : 1"
inkscape:persp3d-origin="8 : 5.3333333 : 1"
id="perspective2391" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3710"
id="linearGradient3716"
x1="7.9066148"
y1="15.937743"
x2="7.9377432"
y2="0.031128405"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
inkscape:window-height="713"
inkscape:window-width="722"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
showgrid="false"
inkscape:zoom="32.125"
inkscape:cx="8"
inkscape:cy="8.154146"
inkscape:window-x="20"
inkscape:window-y="20"
inkscape:current-layer="Foreground" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M7,3h2v2H7V3z M5.5,12H7V8H5.5V7H9v5h1.5v1h-5V12z M0,8c0-4.418,3.582-8,8-8 s8,3.582,8,8s-3.582,8-8,8S0,12.418,0,8z"
id="path2384"
style="fill-opacity:1;fill:url(#linearGradient3716)" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Foreground"
x="0px"
y="0px"
width="29px"
height="18px"
viewBox="0 0 29 18"
enable-background="new 0 0 29 18"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="search_2.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata16"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs14"><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 9 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="29 : 9 : 1"
inkscape:persp3d-origin="14.5 : 6 : 1"
id="perspective18" />
</defs><sodipodi:namedview
inkscape:window-height="728"
inkscape:window-width="1103"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
showgrid="false"
inkscape:zoom="19.275862"
inkscape:cx="14.5"
inkscape:cy="9"
inkscape:window-x="40"
inkscape:window-y="40"
inkscape:current-layer="Foreground"><inkscape:grid
type="xygrid"
id="grid2391" /></sodipodi:namedview>
<path
style="fill:#4669a9;fill-opacity:1"
id="path9"
d="" />
<path
id="path3"
style="fill:#3d5a93;fill-opacity:1"
d="M 0,3 C 0,1.343 1.343,0 3,0 L 20,0 C 20.515,0 21.027,0.195 21.42,0.588 L 28.412,7.58 C 29.196,8.364 29.196,9.636 28.412,10.42 L 21.42,17.412 C 21.028,17.804 20.514,18 20,18 L 3,18 C 1.343,18 0,16.657 0,15 L 0,3 zM 13.5,3 C 11.015,3 9,5.015 9,7.5 C 9,8.2423219 9.1815696,8.9452421 9.5,9.5625 L 6.25,12.8125 C 5.931,13.1325 5.9310002,13.64975 6.25,13.96875 L 7.03125,14.78125 C 7.35025,15.10025 7.8674999,15.10025 8.1875,14.78125 L 11.46875,11.5 C 12.080227,11.810879 12.767137,12 13.5,12 C 15.985,12 18,9.985 18,7.5 C 18,5.015 15.985,3 13.5,3 z M 11.25,7.5 C 11.25,6.257 12.257,5.25 13.5,5.25 C 14.743,5.25 15.75,6.257 15.75,7.5 C 15.75,8.743 14.743,9.75 13.5,9.75 C 12.257,9.75 11.25,8.743 11.25,7.5 z" />
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

79
data/view-more.svg Normal file
View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Foreground"
x="0px"
y="0px"
width="29px"
height="18px"
viewBox="0 0 29 18"
enable-background="new 0 0 29 18"
xml:space="preserve"
sodipodi:version="0.32"
inkscape:version="0.46"
sodipodi:docname="search_1.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
id="metadata16"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
id="defs14"><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 9 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="29 : 9 : 1"
inkscape:persp3d-origin="14.5 : 6 : 1"
id="perspective18" /></defs><sodipodi:namedview
inkscape:window-height="728"
inkscape:window-width="1103"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
guidetolerance="10.0"
gridtolerance="10.0"
objecttolerance="10.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base"
showgrid="false"
inkscape:zoom="19.275862"
inkscape:cx="14.5"
inkscape:cy="9"
inkscape:window-x="40"
inkscape:window-y="40"
inkscape:current-layer="Foreground"><inkscape:grid
type="xygrid"
id="grid2391" /></sodipodi:namedview>
<path
d="M0,3c0-1.657,1.343-3,3-3h17c0.515,0,1.027,0.195,1.42,0.588l6.992,6.992c0.784,0.784,0.784,2.056,0,2.84l-6.992,6.992 C21.028,17.804,20.514,18,20,18H3c-1.657,0-3-1.343-3-3V3z"
id="path3"
style="fill:#151e2f;fill-opacity:1" />
<g
id="g5"
style="fill:#4669a9;fill-opacity:1">
<path
fill="#FFFFFF"
d="M6.246,13.98c-0.319-0.319-0.319-0.837,0-1.157l3.717-3.717c0.319-0.319,0.837-0.319,1.157,0l0.786,0.787 c0.32,0.319,0.32,0.837,0,1.157l-3.717,3.717c-0.32,0.319-0.838,0.319-1.157,0L6.246,13.98L6.246,13.98z"
id="path7"
style="fill:#4669a9;fill-opacity:1" />
<path
fill="#FFFFFF"
d="M9.076,11.937"
id="path9"
style="fill:#4669a9;fill-opacity:1" />
</g>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="#FFFFFF"
d="M11.25,7.5c0-1.243,1.007-2.25,2.25-2.25s2.25,1.007,2.25,2.25 s-1.007,2.25-2.25,2.25S11.25,8.743,11.25,7.5z M9,7.5C9,5.015,11.015,3,13.5,3S18,5.015,18,7.5S15.985,12,13.5,12S9,9.985,9,7.5z"
id="path11"
style="fill:#4669a9;fill-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -7,32 +7,6 @@ const Shell = imports.gi.Shell;
const Main = imports.ui.main;
// TODO - move this into GConf once we're not a plugin anymore
// but have taken over metacity
// This list is taken from GNOME Online popular applications
// http://online.gnome.org/applications
// but with nautilus removed (since it should already be running)
// and evince, totem, and gnome-file-roller removed (since they're
// usually started by opening documents, not by opening the app
// directly)
const DEFAULT_APPLICATIONS = [
'mozilla-firefox.desktop',
'gnome-terminal.desktop',
'evolution.desktop',
'gedit.desktop',
'mozilla-thunderbird.desktop',
'rhythmbox.desktop',
'epiphany.desktop',
'xchat.desktop',
'openoffice.org-1.9-writer.desktop',
'emacs.desktop',
'gnome-system-monitor.desktop',
'openoffice.org-1.9-calc.desktop',
'eclipse.desktop',
'openoffice.org-1.9-impress.desktop',
'vncviewer.desktop'
];
function AppInfo(appId) {
this._init(appId);
}
@ -41,8 +15,9 @@ AppInfo.prototype = {
_init : function(appId) {
this.appId = appId;
this._gAppInfo = Gio.DesktopAppInfo.new(appId);
if (!this._gAppInfo)
if (!this._gAppInfo) {
throw new Error('Unknown appId ' + appId);
}
this.id = this._gAppInfo.get_id();
this.name = this._gAppInfo.get_name();
@ -52,7 +27,7 @@ AppInfo.prototype = {
this._gicon = this._gAppInfo.get_icon();
},
getIcon : function(size) {
createIcon : function(size) {
if (this._gicon)
return Shell.TextureCache.get_default().load_gicon(this._gicon, size);
else
@ -96,40 +71,64 @@ function getAppInfo(appId) {
return info;
}
// getMostUsedApps:
// getTopApps:
// @count: maximum number of apps to retrieve
//
// Gets a list of #AppInfos for the @count most-frequently-used
// applications
// applications, with explicitly-chosen favorites first.
//
// Return value: the list of #AppInfo
function getMostUsedApps(count) {
function getTopApps(count) {
let appMonitor = Shell.AppMonitor.get_default();
let matches = [], alreadyAdded = {};
let favs = getFavorites();
for (let i = 0; i < favs.length && favs.length <= count; i++) {
let appId = favs[i].appId;
if (alreadyAdded[appId])
continue;
alreadyAdded[appId] = true;
matches.push(favs[i]);
}
// Ask for more apps than we need, since the list of recently used
// apps might contain an app we don't have a desktop file for
let apps = appMonitor.get_most_used_apps (0, Math.round(count * 1.5));
let matches = [], alreadyAdded = {};
for (let i = 0; i < apps.length && matches.length <= count; i++) {
let appId = apps[i] + ".desktop";
if (alreadyAdded[appId])
continue;
alreadyAdded[appId] = true;
let appInfo = getAppInfo(appId);
if (appInfo) {
matches.push(appInfo);
alreadyAdded[appId] = true;
}
}
// Fill the list with default applications it's not full yet
for (let i = 0; i < DEFAULT_APPLICATIONS.length && matches.length <= count; i++) {
let appId = DEFAULT_APPLICATIONS[i];
if (alreadyAdded[appId])
continue;
let appInfo = getAppInfo(appId);
if (appInfo)
matches.push(appInfo);
}
return matches;
}
function _idListToInfos(ids) {
let infos = [];
for (let i = 0; i < ids.length; i++) {
let display = getAppInfo(ids[i]);
if (display == null)
continue;
infos.push(display);
}
return infos;
}
function getFavorites() {
let system = Shell.AppSystem.get_default();
return _idListToInfos(system.get_favorites());
}
function getRunning() {
let monitor = Shell.AppMonitor.get_default();
return _idListToInfos(monitor.get_running_app_ids());
}

View File

@ -21,7 +21,7 @@ DocInfo.prototype = {
this.mimeType = recentInfo.get_mime_type();
},
getIcon : function(size) {
createIcon : function(size) {
let icon = new Clutter.Texture();
let iconPixbuf;

View File

@ -5,16 +5,21 @@ const Clutter = imports.gi.Clutter;
const Pango = imports.gi.Pango;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Tidy = imports.gi.Tidy;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
const Mainloop = imports.mainloop;
const AppInfo = imports.misc.appInfo;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const ENTERED_MENU_COLOR = new Clutter.Color();
ENTERED_MENU_COLOR.from_pixel(0x00ff0022);
const APP_ICON_SIZE = 48;
const MENU_ICON_SIZE = 24;
const MENU_SPACING = 15;
@ -33,11 +38,10 @@ AppDisplayItem.prototype = {
__proto__: GenericDisplay.GenericDisplayItem.prototype,
_init : function(appInfo, availableWidth) {
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
this._appInfo = appInfo;
this._setItemInfo(appInfo.name, appInfo.description,
appInfo.getIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE));
this._setItemInfo(appInfo.name, appInfo.description);
},
//// Public method overrides ////
@ -49,15 +53,20 @@ AppDisplayItem.prototype = {
//// Protected method overrides ////
// Returns an icon for the item.
_createIcon : function() {
return this._appInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
},
// Ensures the preview icon is created.
_ensurePreviewIconCreated : function() {
if (!this._showPreview || this._previewIcon)
return;
return;
let previewIconPath = this._appInfo.getIconPath(GenericDisplay.PREVIEW_ICON_SIZE);
if (previewIconPath) {
try {
this._previewIcon = new Clutter.Texture({ width: GenericDisplay.PREVIEW_ICON_SIZE, height: GenericDisplay.PREVIEW_ICON_SIZE});
this._previewIcon = new Clutter.Texture({ width: GenericDisplay.PREVIEW_ICON_SIZE, height: GenericDisplay.PREVIEW_ICON_SIZE});
this._previewIcon.set_from_file(previewIconPath);
} catch (e) {
// we can get an error here if the file path doesn't exist on the system
@ -147,6 +156,7 @@ MenuItem.prototype = {
}
Signals.addSignalMethods(MenuItem.prototype);
/* This class represents a display containing a collection of application items.
* The applications are sorted based on their popularity by default, and based on
* their name if some search filter is applied.
@ -173,16 +183,15 @@ AppDisplay.prototype = {
this._appMonitor = Shell.AppMonitor.get_default();
this._appSystem = Shell.AppSystem.get_default();
this._appsStale = true;
this._appSystem.connect('changed', Lang.bind(this, function(appSys) {
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
this._appsStale = true;
// We still need to determine what events other than search can trigger
// a change in the set of applications that are being shown while the
// user in in the overlay mode, however let's redisplay just in case.
this._redisplay(false);
this._redisplayMenus();
}));
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
this._redisplay(false);
}));
this._appMonitor.connect('changed', Lang.bind(this, function(monitor) {
this._appsStale = true;
this._redisplay(false);
}));
@ -308,8 +317,6 @@ AppDisplay.prototype = {
this._appCategories[appId] = categories;
},
//// Protected method overrides ////
// Gets information about all applications by calling Gio.app_info_get_all().
_refreshCache : function() {
let me = this;
@ -339,21 +346,12 @@ AppDisplay.prototype = {
this._addAppForId(appId);
}
// Some applications, such as Evince, might not be in the menus,
// but might be returned by the applications monitor as most used
// applications, in which case we include them.
let mostUsedAppInfos = AppInfo.getMostUsedApps(MAX_ITEMS);
for (let i = 0; i < mostUsedAppInfos.length; i++) {
let appInfo = mostUsedAppInfos[i];
this._addApp(appInfo);
}
this._appsStale = false;
},
// Sets the list of the displayed items based on the most used apps.
_setDefaultList : function() {
let matchedInfos = AppInfo.getMostUsedApps(MAX_ITEMS);
let matchedInfos = AppInfo.getTopApps(MAX_ITEMS);
this._matchedItems = matchedInfos.map(function(info) { return info.appId; });
},
@ -430,3 +428,197 @@ AppDisplay.prototype = {
};
Signals.addSignalMethods(AppDisplay.prototype);
function WellDisplayItem(appInfo, isFavorite) {
this._init(appInfo, isFavorite);
}
WellDisplayItem.prototype = {
_init : function(appInfo, isFavorite) {
this.appInfo = appInfo;
this.isFavorite = isFavorite;
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
width: APP_ICON_SIZE,
reactive: true });
this.actor._delegate = this;
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
this.launch();
this.emit('activated');
}));
let draggable = DND.makeDraggable(this.actor);
this._icon = appInfo.createIcon(APP_ICON_SIZE);
this.actor.append(this._icon, Big.BoxPackFlags.NONE);
let count = Shell.AppMonitor.get_default().get_window_count(appInfo.appId);
this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 12px",
ellipsize: Pango.EllipsizeMode.END,
text: appInfo.name });
if (count > 0) {
let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
border: 1,
padding: 1 */ });
runningBox.append(this._name, Big.BoxPackFlags.EXPAND);
this.actor.append(runningBox, Big.BoxPackFlags.NONE);
} else {
this.actor.append(this._name, Big.BoxPackFlags.NONE);
}
},
// Opens an application represented by this display item.
launch : function() {
this.appInfo.launch();
},
// Draggable interface - FIXME deduplicate with GenericDisplay
getDragActor: function(stageX, stageY) {
this.dragActor = this.appInfo.createIcon(APP_ICON_SIZE);
// If the user dragged from the icon itself, then position
// the dragActor over the original icon. Otherwise center it
// around the pointer
let [iconX, iconY] = this._icon.get_transformed_position();
let [iconWidth, iconHeight] = this._icon.get_transformed_size();
if (stageX > iconX && stageX <= iconX + iconWidth &&
stageY > iconY && stageY <= iconY + iconHeight)
this.dragActor.set_position(iconX, iconY);
else
this.dragActor.set_position(stageX - this.dragActor.width / 2, stageY - this.dragActor.height / 2);
return this.dragActor;
},
// Returns the original icon that is being used as a source for the cloned texture
// that represents the item as it is being dragged.
getDragActorSource: function() {
return this._icon;
}
};
Signals.addSignalMethods(WellDisplayItem.prototype);
function WellArea(width, isFavorite) {
this._init(width, isFavorite);
}
WellArea.prototype = {
_init : function(width, isFavorite) {
this.isFavorite = isFavorite;
this.actor = new Tidy.Grid({ width: width });
this.actor._delegate = this;
},
redisplay: function (infos) {
let children;
children = this.actor.get_children();
children.forEach(Lang.bind(this, function (v) {
v.destroy();
}));
for (let i = 0; i < infos.length; i++) {
let display = new WellDisplayItem(infos[i], this.isFavorite);
display.connect('activated', Lang.bind(this, function (display) {
this.emit('activated', display);
}));
this.actor.add_actor(display.actor);
};
},
// Draggable target interface
acceptDrop : function(source, actor, x, y, time) {
let global = Shell.Global.get();
if (!(source instanceof WellDisplayItem)) {
return false;
}
let appSystem = Shell.AppSystem.get_default();
let id = source.appInfo.appId;
if (source.isFavorite && (!this.isFavorite)) {
Mainloop.idle_add(function () {
appSystem.remove_favorite(id);
});
} else if ((!source.isFavorite) && this.isFavorite) {
Mainloop.idle_add(function () {
appSystem.add_favorite(id);
});
} else {
return false;
}
return true;
}
}
Signals.addSignalMethods(WellArea.prototype);
function AppWell(width) {
this._init(width);
}
AppWell.prototype = {
_init : function(width) {
this._menus = [];
this._menuDisplays = [];
this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
width: width });
this._appSystem = Shell.AppSystem.get_default();
this._appMonitor = Shell.AppMonitor.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, function(appSys) {
this._redisplay();
}));
this._appSystem.connect('favorites-changed', Lang.bind(this, function(appSys) {
this._redisplay();
}));
this._appMonitor.connect('changed', Lang.bind(this, function(monitor) {
this._redisplay();
}));
this._favoritesArea = new WellArea(width, true);
this._favoritesArea.connect('activated', Lang.bind(this, function (a, display) {
this.emit('activated');
}));
this.actor.append(this._favoritesArea.actor, Big.BoxPackFlags.NONE);
this._runningBox = new Big.Box({ border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
border: 1,
corner_radius: 3,
padding: GenericDisplay.PREVIEW_BOX_PADDING });
this._runningArea = new WellArea(width, false);
this._runningArea.connect('activated', Lang.bind(this, function (a, display) {
this.emit('activated');
}));
this._runningBox.append(this._runningArea.actor, Big.BoxPackFlags.EXPAND);
this.actor.append(this._runningBox, Big.BoxPackFlags.NONE);
this._redisplay();
},
_redisplay: function() {
let arrayToObject = function(a) {
let o = {};
for (let i = 0; i < a.length; i++)
o[a[i]] = 1;
return o;
};
let favorites = AppInfo.getFavorites();
let favoriteIds = arrayToObject(favorites.map(function (e) { return e.appId; }));
let running = AppInfo.getRunning().filter(function (e) {
return !(e.appId in favoriteIds);
});
this._favoritesArea.redisplay(favorites);
this._runningArea.redisplay(running);
}
};
Signals.addSignalMethods(AppWell.prototype);

View File

@ -27,8 +27,7 @@ DocDisplayItem.prototype = {
GenericDisplay.GenericDisplayItem.prototype._init.call(this, availableWidth);
this._docInfo = docInfo;
this._setItemInfo(docInfo.name, "",
docInfo.getIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE));
this._setItemInfo(docInfo.name, "");
},
//// Public methods ////
@ -42,10 +41,15 @@ DocDisplayItem.prototype = {
//// Protected method overrides ////
// Returns an icon for the item.
_createIcon : function() {
return this._docInfo.createIcon(GenericDisplay.ITEM_DISPLAY_ICON_SIZE);
},
// Ensures the preview icon is created.
_ensurePreviewIconCreated : function() {
if (!this._previewIcon)
this._previewIcon = this._docInfo.getIcon(GenericDisplay.PREVIEW_ICON_SIZE);
this._previewIcon = this._docInfo.createIcon(GenericDisplay.PREVIEW_ICON_SIZE);
},
// Creates and returns a large preview icon, but only if this._docInfo is an image file

View File

@ -22,7 +22,7 @@ ITEM_DISPLAY_DESCRIPTION_COLOR.from_pixel(0xffffffbb);
const ITEM_DISPLAY_BACKGROUND_COLOR = new Clutter.Color();
ITEM_DISPLAY_BACKGROUND_COLOR.from_pixel(0x00000000);
const ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR = new Clutter.Color();
ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x00ff0055);
ITEM_DISPLAY_SELECTED_BACKGROUND_COLOR.from_pixel(0x4f6fadaa);
const DISPLAY_CONTROL_SELECTED_COLOR = new Clutter.Color();
DISPLAY_CONTROL_SELECTED_COLOR.from_pixel(0x112288ff);
const PREVIEW_BOX_BACKGROUND_COLOR = new Clutter.Color();
@ -30,7 +30,8 @@ PREVIEW_BOX_BACKGROUND_COLOR.from_pixel(0xADADADf0);
const ITEM_DISPLAY_HEIGHT = 50;
const ITEM_DISPLAY_ICON_SIZE = 48;
const ITEM_DISPLAY_PADDING = 1;
const ITEM_DISPLAY_PADDING_TOP = 1;
const ITEM_DISPLAY_PADDING_RIGHT = 2;
const DEFAULT_COLUMN_GAP = 6;
const LABEL_HEIGHT = 16;
@ -42,6 +43,8 @@ const PREVIEW_BOX_CORNER_RADIUS = 10;
const PREVIEW_PLACING = 3/4;
const PREVIEW_DETAILS_MIN_WIDTH = PREVIEW_ICON_SIZE * 2;
const INFORMATION_BUTTON_SIZE = 16;
/* This is a virtual class that represents a single display item containing
* a name, a description, and an icon. It allows selecting an item and represents
* it by highlighting it with a different background color than the default.
@ -55,22 +58,17 @@ function GenericDisplayItem(availableWidth) {
GenericDisplayItem.prototype = {
_init: function(availableWidth) {
this._availableWidth = availableWidth;
this._showPreview = false;
this._havePointer = false;
this._previewEventSourceId = null;
this.actor = new Clutter.Group({ reactive: true,
width: availableWidth,
height: ITEM_DISPLAY_HEIGHT });
this.actor._delegate = this;
this.actor.connect('button-press-event',
Lang.bind(this,
function(actor, e) {
let clickCount = Shell.get_button_event_click_count(e);
if (clickCount == 1)
this.select();
else if (clickCount == 2)
this.activate();
this.actor.connect('button-release-event',
Lang.bind(this,
function() {
// Activates the item by launching it
this.emit('activate');
return true;
}));
let draggable = DND.makeDraggable(this.actor);
@ -81,11 +79,40 @@ GenericDisplayItem.prototype = {
x: 0, y: 0,
width: availableWidth, height: ITEM_DISPLAY_HEIGHT });
this.actor.add_actor(this._bg);
let global = Shell.Global.get();
let infoIconUri = "file://" + global.imagedir + "info.svg";
this._informationButton = Shell.TextureCache.get_default().load_uri_sync(infoIconUri,
INFORMATION_BUTTON_SIZE,
INFORMATION_BUTTON_SIZE);
this._informationButton.x = availableWidth - ITEM_DISPLAY_PADDING_RIGHT - INFORMATION_BUTTON_SIZE;
this._informationButton.y = ITEM_DISPLAY_HEIGHT / 2 - INFORMATION_BUTTON_SIZE / 2;
this._informationButton.reactive = true;
// Connecting to the button-press-event for the information button ensures that the actor,
// which is a draggable actor, does not get the button-press-event and doesn't initiate
// the dragging, which then prevents us from getting the button-release-event for the button.
this._informationButton.connect('button-press-event',
Lang.bind(this,
function() {
return true;
}));
this._informationButton.connect('button-release-event',
Lang.bind(this,
function() {
// Selects the item by highlighting it and displaying its details
this.emit('select');
return true;
}));
this._informationButton.hide();
this.actor.add_actor(this._informationButton);
this._informationButton.lower_bottom();
this._name = null;
this._description = null;
this._icon = null;
this._preview = null;
this._previewIcon = null;
this.dragActor = null;
@ -99,8 +126,7 @@ GenericDisplayItem.prototype = {
// Returns a cloned texture of the item's icon to represent the item as it
// is being dragged.
getDragActor: function(stageX, stageY) {
this.dragActor = new Clutter.Clone({ source: this._icon });
[this.dragActor.width, this.dragActor.height] = this._icon.get_transformed_size();
this.dragActor = this._createIcon();
// If the user dragged from the icon itself, then position
// the dragActor over the original icon. Otherwise center it
@ -115,58 +141,19 @@ GenericDisplayItem.prototype = {
return this.dragActor;
},
// Returns the original icon that is being used as a source for the cloned texture
// that represents the item as it is being dragged.
// Returns the item icon, a separate copy of which is used to
// represent the item as it is being dragged. This is used to
// determine a snap-back location for the drag icon if it does
// not get accepted by any drop target.
getDragActorSource: function() {
return this._icon;
},
//// Public methods ////
// Sets a boolean value that indicates whether the item should display a pop-up preview on mouse over.
setShowPreview: function(showPreview) {
this._showPreview = showPreview;
},
// Returns a boolean value that indicates whether the item displays a pop-up preview on mouse over.
getShowPreview: function() {
return this._showPreview;
},
// Displays the preview for the item.
showPreview: function() {
if(!this._showPreview)
return;
this._ensurePreviewCreated();
let [x, y] = this.actor.get_transformed_position();
let global = Shell.Global.get();
let previewX = Math.min(x + this._availableWidth * PREVIEW_PLACING, global.screen_width - this._preview.width);
let previewY = Math.min(y, global.screen_height - this._preview.height);
this._preview.set_position(previewX, previewY);
this._preview.show();
},
// Hides the preview for the item and removes the preview event source so that
// there is no preview scheduled to show up.
hidePreview: function() {
if (this._previewEventSourceId) {
Mainloop.source_remove(this._previewEventSourceId);
this._previewEventSourceId = null;
}
if (this._preview)
this._preview.hide();
},
// Shows a preview when the item was drawn under the mouse pointer.
// Shows the information button when the item was drawn under the mouse pointer.
onDrawnUnderPointer: function() {
this._havePointer = true;
// This code is usually triggered when we just had a different preview showing on the same spot
// and having a delay before showing a new preview looks bad. So we just show it right away.
this.showPreview();
this._informationButton.show();
},
// Highlights the item by setting a different background color than the default
@ -180,17 +167,6 @@ GenericDisplayItem.prototype = {
this._bg.background_color = color;
},
// Activates the item, as though it was launched
activate: function() {
this.hidePreview();
this.emit('activate');
},
// Selects the item, as though it was clicked
select: function() {
this.emit('select');
},
/*
* Returns an actor containing item details. In the future details can have more information than what
* the preview pop-up has and be item-type specific.
@ -199,7 +175,7 @@ GenericDisplayItem.prototype = {
* availableHeight - height available for displaying details
*/
createDetailsActor: function(availableWidth, availableHeight) {
let details = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PREVIEW_BOX_SPACING,
width: availableWidth });
@ -241,28 +217,12 @@ GenericDisplayItem.prototype = {
details.append(largePreview, Big.BoxPackFlags.NONE);
}
// We hide the preview pop-up if the details are shown elsewhere.
details.connect("show",
Lang.bind(this,
function() {
// Right now "show" signal is emitted when an actor is added to a parent that
// has not been added to anything and "visible" property is also set to true
// at this point, so checking if the parent that the actor has been added to
// has a parent of its own is a temporary workaround. That other actor is
// presumed to be displayed, which is a limitation of this workaround, but is
// the case with our usage of the details actor now.
// http://bugzilla.openedhand.com/show_bug.cgi?id=1138
if (details.get_parent() != null && details.get_parent().get_parent() != null)
this.hidePreview();
}));
return details;
},
// Destoys the item, as well as a preview for the item if it exists.
// Destoys the item.
destroy: function() {
this.actor.destroy();
if (this._preview != null)
this._preview.destroy();
},
//// Pure virtual public methods ////
@ -279,9 +239,8 @@ GenericDisplayItem.prototype = {
*
* nameText - name of the item
* descriptionText - short description of the item
* iconActor - ClutterTexture containing the icon image which should be ITEM_DISPLAY_ICON_SIZE size
*/
_setItemInfo: function(nameText, descriptionText, iconActor) {
_setItemInfo: function(nameText, descriptionText) {
if (this._name != null) {
// this also removes this._name from the parent container,
// so we don't need to call this.actor.remove_actor(this._name) directly
@ -298,27 +257,23 @@ GenericDisplayItem.prototype = {
this._icon.destroy();
this._icon = null;
}
// This ensures we'll create a new preview and previewIcon next time we need a preview
if (this._preview != null) {
this._preview.destroy();
this._preview = null;
}
// This ensures we'll create a new previewIcon next time we need it
if (this._previewIcon != null) {
this._previewIcon.destroy();
this._previewIcon = null;
}
this._icon = iconActor;
this._icon = this._createIcon();
this.actor.add_actor(this._icon);
let textWidth = this._availableWidth - (ITEM_DISPLAY_ICON_SIZE + 4);
let textWidth = this._availableWidth - (ITEM_DISPLAY_ICON_SIZE + 4) - INFORMATION_BUTTON_SIZE - ITEM_DISPLAY_PADDING_RIGHT;
this._name = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 14px",
width: textWidth,
ellipsize: Pango.EllipsizeMode.END,
text: nameText,
x: ITEM_DISPLAY_ICON_SIZE + 4,
y: ITEM_DISPLAY_PADDING });
y: ITEM_DISPLAY_PADDING_TOP });
this.actor.add_actor(this._name);
this._description = new Clutter.Text({ color: ITEM_DISPLAY_DESCRIPTION_COLOR,
font_name: "Sans 12px",
@ -339,6 +294,11 @@ GenericDisplayItem.prototype = {
//// Pure virtual protected methods ////
// Returns an icon for the item.
_createIcon: function() {
throw new Error("Not implemented");
},
// Ensures the preview icon is created.
_ensurePreviewIconCreated: function() {
throw new Error("Not implemented");
@ -346,79 +306,21 @@ GenericDisplayItem.prototype = {
//// Private methods ////
// Ensures the preview actor is created.
_ensurePreviewCreated: function() {
if (!this._showPreview || this._preview)
return;
this._preview = new Big.Box({ background_color: PREVIEW_BOX_BACKGROUND_COLOR,
orientation: Big.BoxOrientation.HORIZONTAL,
corner_radius: PREVIEW_BOX_CORNER_RADIUS,
padding: PREVIEW_BOX_PADDING,
spacing: PREVIEW_BOX_SPACING });
let textDetailsWidth = this._availableWidth - PREVIEW_BOX_PADDING * 2;
this._ensurePreviewIconCreated();
if (this._previewIcon != null) {
this._preview.append(this._previewIcon, Big.BoxPackFlags.EXPAND);
textDetailsWidth = this._availableWidth - this._previewIcon.width - PREVIEW_BOX_PADDING * 2 - PREVIEW_BOX_SPACING;
}
// Inner box with name and description
let textDetails = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
spacing: PREVIEW_BOX_SPACING });
let detailsName = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans bold 14px",
text: this._name.text});
textDetails.width = Math.max(PREVIEW_DETAILS_MIN_WIDTH, textDetailsWidth, detailsName.width);
textDetails.append(detailsName, Big.BoxPackFlags.NONE);
let detailsDescription = new Clutter.Text({ color: ITEM_DISPLAY_NAME_COLOR,
font_name: "Sans 14px",
line_wrap: true,
text: this._description.text });
textDetails.append(detailsDescription, Big.BoxPackFlags.NONE);
this._preview.append(textDetails, Big.BoxPackFlags.EXPAND);
// Add the preview to global stage to allow for top-level layering
let global = Shell.Global.get();
global.stage.add_actor(this._preview);
this._preview.hide();
},
// Performs actions on mouse enter event for the item. Currently, shows the preview for the item.
// Performs actions on mouse enter event for the item. Currently, shows the information button for the item.
_onEnter: function(actor, event) {
this._havePointer = true;
let tooltipTimeout = Gtk.Settings.get_default().gtk_tooltip_timeout;
this._previewEventSourceId = Mainloop.timeout_add(tooltipTimeout,
Lang.bind(this,
function() {
if (this._havePointer) {
this.showPreview();
}
this._previewEventSourceId = null;
return false;
}));
this._informationButton.show();
},
// Performs actions on mouse leave event for the item. Currently, hides the preview for the item.
// Performs actions on mouse leave event for the item. Currently, hides the information button for the item.
_onLeave: function(actor, event) {
this._havePointer = false;
this.hidePreview();
this._informationButton.hide();
},
// Hides the preview once the item starts being dragged.
// Hides the information button once the item starts being dragged.
_onDragBegin : function (draggable, time) {
// For some reason, we are not getting leave-event signal when we are dragging an item,
// so the preview box stays behind if we didn't have the call here. It makes sense to hide
// the preview as soon as the item starts being dragged anyway.
this._havePointer = false;
this.hidePreview();
// so we should remove the link manually.
this._informationButton.hide();
}
};
@ -469,8 +371,6 @@ GenericDisplay.prototype = {
// See also getSideArea.
this.actor = this._grid;
this.displayControl = new Big.Box({ background_color: ITEM_DISPLAY_BACKGROUND_COLOR,
corner_radius: 4,
height: 24,
spacing: 12,
orientation: Big.BoxOrientation.HORIZONTAL});
@ -557,14 +457,6 @@ GenericDisplay.prototype = {
this._selectIndex(-1);
},
// Hides the preview if any item has one being displayed.
hidePreview: function() {
for (itemId in this._displayedItems) {
let item = this._displayedItems[itemId];
item.hidePreview();
}
},
// Returns true if the display has any displayed items.
hasItems: function() {
return this._displayedItemsCount > 0;
@ -645,13 +537,13 @@ GenericDisplay.prototype = {
Mainloop.timeout_add(5,
Lang.bind(this,
function() {
// Check if the pointer is over one of the items and display the preview pop-up if it is.
// Check if the pointer is over one of the items and display the information button if it is.
let [child, x, y, mask] = Gdk.Screen.get_default().get_root_window().get_pointer();
let global = Shell.Global.get();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE,
x, y);
if (actor != null) {
let item = this._findDisplayedByActor(actor.get_parent());
let item = this._findDisplayedByActor(actor);
if (item != null) {
item.onDrawnUnderPointer();
}
@ -670,7 +562,6 @@ GenericDisplay.prototype = {
let itemInfo = this._allItems[itemId];
let displayItem = this._createDisplayItem(itemInfo);
displayItem.setShowPreview(true);
displayItem.connect('activate',
Lang.bind(this,
@ -702,32 +593,8 @@ GenericDisplay.prototype = {
this.selectUp();
}
if (displayItem.dragActor) {
// The user might be handling a dragActor when the list of items
// changes (for example, if the dragging caused us to transition
// from an expanded overlay view to the regular view). So we need
// to keep the item around so that the drag and drop action initiated
// by the user can be completed. However, we remove the item from the list.
//
// For some reason, just removing the displayItem.actor
// is not enough to get displayItem._icon.visible
// to return false, so we hide the display item and
// all its children first. (We check displayItem._icon.visible
// when deciding if a dragActor has a place to snap back to
// in case the drop was not accepted by any actor.)
displayItem.actor.hide_all();
this._grid.remove_actor(displayItem.actor);
// We should not destroy the item up-front, because that would also
// destroy the icon that was used to clone the image for the drag actor.
// We destroy it once the dragActor is destroyed instead.
displayItem.dragActor.connect('destroy',
function(item) {
displayItem.destroy();
});
} else {
displayItem.destroy();
}
displayItem.destroy();
delete this._displayedItems[itemId];
this._displayedItemsCount--;
},

File diff suppressed because it is too large Load Diff

View File

@ -182,7 +182,7 @@ LauncherWidget.prototype = {
spacing: ITEM_SPACING,
reactive: true });
item._info = info;
item.append(info.getIcon(ITEM_ICON_SIZE), Big.BoxPackFlags.NONE);
item.append(info.createIcon(ITEM_ICON_SIZE), Big.BoxPackFlags.NONE);
item.append(new Clutter.Text({ color: ITEM_NAME_COLOR,
font_name: "Sans 14px",
ellipsize: Pango.EllipsizeMode.END,
@ -203,7 +203,7 @@ LauncherWidget.prototype = {
padding: ITEM_PADDING,
reactive: true });
item._info = info;
item.append(info.getIcon(COLLAPSED_WIDTH - 2 * ITEM_PADDING),
item.append(info.createIcon(COLLAPSED_WIDTH - 2 * ITEM_PADDING),
Big.BoxPackFlags.NONE);
this.collapsedActor.append(item, Big.BoxPackFlags.NONE);
@ -284,7 +284,7 @@ AppsWidget.prototype = {
this.actor = new Big.Box({ spacing: 2 });
this.collapsedActor = new Big.Box({ spacing: 2});
let apps = AppInfo.getMostUsedApps(5);
let apps = AppInfo.getTopApps(5);
for (let i = 0; i < apps.length; i++)
this.addItem(apps[i]);
}

View File

@ -11,6 +11,7 @@ const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const AppDisplay = imports.ui.appDisplay;
const DND = imports.ui.dnd;
const GenericDisplay = imports.ui.genericDisplay;
const Main = imports.ui.main;
@ -752,7 +753,7 @@ Workspace.prototype = {
false, // don't create workspace
time);
return true;
} else if (source instanceof GenericDisplay.GenericDisplayItem) {
} else if (source instanceof GenericDisplay.GenericDisplayItem || source instanceof AppDisplay.WellDisplayItem) {
this._metaWorkspace.activate(time);
source.launch();
return true;

View File

@ -13,9 +13,11 @@
#include "shell-app-monitor.h"
#include "shell-app-system.h"
#include "shell-global.h"
#include "display.h"
#include "window.h"
/* This file includes modified code from
* desktop-data-engine/engine-dbus/hippo-application-monitor.c
@ -63,6 +65,7 @@ static struct
const char *pattern;
GRegex *regex;
} title_patterns[] = {
{"mozilla-firefox", ".* - Mozilla Firefox", NULL}, \
{"openoffice.org-writer", ".* - OpenOffice.org Writer$", NULL}, \
{"openoffice.org-calc", ".* - OpenOffice.org Calc$", NULL}, \
{"openoffice.org-impress", ".* - OpenOffice.org Impress$", NULL}, \
@ -93,6 +96,12 @@ struct _ShellAppMonitor
gboolean currently_idle;
gboolean enable_monitoring;
/* <char * appid, guint window_count> */
GHashTable *running_appids;
/* <MetaWindow * window, char * appid> */
GHashTable *window_to_appid;
GHashTable *apps_by_wm_class; /* Seen apps by wm_class */
GHashTable *popularities; /* One AppPopularity struct list per activity */
int upload_apps_burst_count;
@ -191,6 +200,232 @@ destroy_popularity (gpointer key,
g_slist_free (list);
}
static char *
get_wmclass_for_window (MetaWindow *window)
{
static gboolean patterns_initialized = FALSE;
const char *wm_class;
char *title;
int i;
wm_class = meta_window_get_wm_class (window);
g_object_get (window, "title", &title, NULL);
if (!patterns_initialized) /* Generate match patterns once for all */
{
patterns_initialized = TRUE;
for (i = 0; title_patterns[i].app_id; i++)
{
title_patterns[i].regex = g_regex_new (title_patterns[i].pattern,
0, 0, NULL);
}
}
/* Match window title patterns to identifiers for non-standard apps */
if (title)
{
for (i = 0; title_patterns[i].app_id; i++)
{
if (g_regex_match (title_patterns[i].regex, title, 0, NULL))
{
/* Set a pseudo WM class, handled like true ones */
wm_class = title_patterns[i].app_id;
break;
}
}
}
g_free (title);
return g_strdup (wm_class);
}
/**
* get_cleaned_wmclass_for_window:
*
* A "cleaned" wmclass is the WM_CLASS property of a window,
* after some transformations to turn it into a form
* somewhat more resilient to changes, such as lowercasing.
*/
static char *
get_cleaned_wmclass_for_window (MetaWindow *window)
{
char *wmclass;
char *cleaned_wmclass;
if (meta_window_get_window_type (window) != META_WINDOW_NORMAL)
return NULL;
wmclass = get_wmclass_for_window (window);
if (!wmclass)
return NULL;
cleaned_wmclass = g_utf8_strdown (wmclass, -1);
g_free (wmclass);
/* This handles "Fedora Eclipse", probably others */
g_strdelimit (cleaned_wmclass, " ", '-');
wmclass = g_strdup (cleaned_wmclass);
g_free (cleaned_wmclass);
return wmclass;
}
/**
* get_appid_for_window:
*
* Returns a desktop file ID for an application, or %NULL if
* we're unable to determine one.
*/
static char *
get_appid_for_window (MetaWindow *window)
{
char *wmclass;
char *with_desktop;
char *result;
ShellAppSystem *appsys;
wmclass = get_cleaned_wmclass_for_window (window);
if (!wmclass)
return NULL;
with_desktop = g_strjoin (NULL, wmclass, ".desktop", NULL);
g_free (wmclass);
appsys = shell_app_system_get_default ();
result = shell_app_system_lookup_basename (appsys, with_desktop);
g_free (with_desktop);
return result;
}
static void
track_window (ShellAppMonitor *self,
MetaWindow *window)
{
char *appid;
guint window_count;
appid = get_appid_for_window (window);
if (!appid)
return;
g_hash_table_insert (self->window_to_appid, window, appid);
window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
window_count += 1;
g_hash_table_insert (self->running_appids, g_strdup (appid), GUINT_TO_POINTER (window_count));
if (window_count == 1)
g_signal_emit (self, signals[CHANGED], 0);
}
static void
shell_app_monitor_on_window_added (MetaWorkspace *workspace,
MetaWindow *window,
gpointer user_data)
{
ShellAppMonitor *self = SHELL_APP_MONITOR (user_data);
track_window (self, window);
}
static void
shell_app_monitor_on_window_removed (MetaWorkspace *workspace,
MetaWindow *window,
gpointer user_data)
{
ShellAppMonitor *self = SHELL_APP_MONITOR (user_data);
char *appid;
guint window_count;
appid = g_hash_table_lookup (self->window_to_appid, window);
if (!appid)
return;
window_count = GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
window_count -= 1;
if (window_count == 0)
{
g_hash_table_remove (self->running_appids, appid);
g_free (appid);
g_signal_emit (self, signals[CHANGED], 0);
}
else
{
g_hash_table_insert (self->running_appids, appid, GUINT_TO_POINTER (window_count));
}
g_hash_table_remove (self->window_to_appid, window);
}
static void
load_initial_windows (ShellAppMonitor *monitor)
{
GList *workspaces, *iter;
MetaScreen *screen = shell_global_get_screen (shell_global_get ());
workspaces = meta_screen_get_workspaces (screen);
for (iter = workspaces; iter; iter = iter->next)
{
MetaWorkspace *workspace = iter->data;
GList *windows = meta_workspace_list_windows (workspace);
GList *window_iter;
for (window_iter = windows; window_iter; window_iter = window_iter->next)
track_window (monitor, (MetaWindow*)window_iter->data);
g_list_free (windows);
}
}
guint
shell_app_monitor_get_window_count (ShellAppMonitor *self,
const char *appid)
{
return GPOINTER_TO_UINT (g_hash_table_lookup (self->running_appids, appid));
}
static void
shell_app_monitor_on_n_workspaces_changed (MetaScreen *screen,
GParamSpec *pspec,
gpointer user_data)
{
ShellAppMonitor *self = SHELL_APP_MONITOR (user_data);
GList *workspaces, *iter;
workspaces = meta_screen_get_workspaces (screen);
for (iter = workspaces; iter; iter = iter->next)
{
MetaWorkspace *workspace = iter->data;
/* This pair of disconnect/connect is idempotent if we were
* already connected, while ensuring we get connected for
* new workspaces.
*/
g_signal_handlers_disconnect_by_func (workspace,
shell_app_monitor_on_window_added,
self);
g_signal_handlers_disconnect_by_func (workspace,
shell_app_monitor_on_window_removed,
self);
g_signal_connect (workspace, "window-added",
G_CALLBACK (shell_app_monitor_on_window_added), self);
g_signal_connect (workspace, "window-removed",
G_CALLBACK (shell_app_monitor_on_window_removed), self);
}
}
static void
init_window_monitoring (ShellAppMonitor *self)
{
MetaScreen *screen = shell_global_get_screen (shell_global_get ());
g_signal_connect (screen, "notify::n-workspaces",
G_CALLBACK (shell_app_monitor_on_n_workspaces_changed), self);
shell_app_monitor_on_n_workspaces_changed (screen, NULL, self);
}
static void
shell_app_monitor_init (ShellAppMonitor *self)
{
@ -223,6 +458,15 @@ shell_app_monitor_init (ShellAppMonitor *self)
(GDestroyNotify) g_free,
(GDestroyNotify) g_free);
self->running_appids = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
self->window_to_appid = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) g_free);
load_initial_windows (self);
init_window_monitoring (self);
g_object_get (shell_global_get(), "configdir", &shell_config_dir, NULL),
path = g_build_filename (shell_config_dir, DATA_FILENAME, NULL);
g_free (shell_config_dir);
@ -299,16 +543,31 @@ shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
return list;
}
/* Find the active window in order to collect stats */
void
get_active_app_properties (ShellAppMonitor *monitor,
char **wm_class,
char **title)
/**
* shell_app_monitor_get_running_app_ids:
*
* @monitor: An app monitor instance
*
* Returns: (element-type utf8) (transfer container): List of application desktop
* identifiers
*/
GList *
shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor)
{
return g_hash_table_get_keys (monitor->running_appids);
}
void
update_app_info (ShellAppMonitor *monitor)
{
char *wm_class;
ShellGlobal *global;
GHashTable *app_active_times = NULL; /* last active time for an application */
MetaScreen *screen;
MetaDisplay *display;
MetaWindow *active;
int activity;
guint32 timestamp;
global = shell_global_get ();
g_object_get (global, "screen", &screen, NULL);
@ -317,60 +576,14 @@ get_active_app_properties (ShellAppMonitor *monitor,
active = meta_display_get_focus_window (display);
if (wm_class)
*wm_class = NULL;
if (title)
*title = NULL;
if (active == NULL)
return;
*wm_class = g_strdup (meta_window_get_wm_class (active));
g_object_get (active, "title", title, NULL);
}
wm_class = get_cleaned_wmclass_for_window (active);
void
update_app_info (ShellAppMonitor *monitor)
{
char *wm_class;
char *title;
GHashTable *app_active_times = NULL; /* GTime spent per activity */
static gboolean first_time = TRUE;
int activity;
guint32 timestamp;
int i;
if (first_time) /* Generate match patterns once for all */
{
first_time = FALSE;
for (i = 0; title_patterns[i].app_id; i++)
{
title_patterns[i].regex = g_regex_new (title_patterns[i].pattern,
0, 0, NULL);
}
}
get_active_app_properties (monitor, &wm_class, &title);
/* Match window title patterns to identifiers for non-standard apps */
if (title)
{
for (i = 0; title_patterns[i].app_id; i++)
{
if ( g_regex_match (title_patterns[i].regex, title, 0, NULL) )
{
/* Set a pseudo WM class, handled like true ones */
g_free (wm_class);
wm_class = g_strdup(title_patterns[i].app_id);
break;
}
}
g_free (title);
}
if (!wm_class)
return;
app_active_times = g_hash_table_lookup (monitor->apps_by_wm_class, wm_class);
if (!app_active_times)
{

View File

@ -40,9 +40,10 @@ GSList *shell_app_monitor_get_most_used_apps (ShellAppMonitor *monitor,
int activity,
gint number);
guint shell_app_monitor_get_window_count (ShellAppMonitor *monitor, const char *appid);
/* Get whatever's running right now */
GSList *shell_app_monitor_get_running_apps (ShellAppMonitor *monitor,
int activity);
GList *shell_app_monitor_get_running_app_ids (ShellAppMonitor *monitor);
G_END_DECLS

View File

@ -1,19 +1,25 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#include "shell-app-system.h"
#include <string.h>
#include <gio/gio.h>
#include <gconf/gconf.h>
#include <gconf/gconf-client.h>
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
#include <gmenu-tree.h>
#define SHELL_APP_FAVORITES_KEY "/desktop/gnome/shell/favorite_apps"
enum {
PROP_0,
};
enum {
CHANGED,
INSTALLED_CHANGED,
FAVORITES_CHANGED,
LAST_SIGNAL
};
@ -26,11 +32,17 @@ struct _ShellAppSystemPrivate {
GSList *cached_app_menus; /* ShellAppMenuEntry */
GSList *cached_setting_ids; /* utf8 */
GHashTable *cached_favorites; /* <utf8,integer> */
gint app_monitor_id;
};
static void shell_app_system_finalize (GObject *object);
static void on_tree_changed (GMenuTree *tree, gpointer user_data);
static void reread_menus (ShellAppSystem *self);
static void on_favorite_apps_changed (GConfClient *client, guint id, GConfEntry *entry, gpointer user_data);
static void reread_favorite_apps (ShellAppSystem *system);
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
@ -63,14 +75,22 @@ static void shell_app_system_class_init(ShellAppSystemClass *klass)
gobject_class->finalize = shell_app_system_finalize;
signals[CHANGED] =
g_signal_new ("changed",
signals[INSTALLED_CHANGED] =
g_signal_new ("installed-changed",
SHELL_TYPE_APP_SYSTEM,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ShellAppSystemClass, changed),
G_STRUCT_OFFSET (ShellAppSystemClass, installed_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[FAVORITES_CHANGED] =
g_signal_new ("favorites-changed",
SHELL_TYPE_APP_SYSTEM,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ShellAppSystemClass, favorites_changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_type_class_add_private (gobject_class, sizeof (ShellAppSystemPrivate));
}
@ -79,10 +99,16 @@ static void
shell_app_system_init (ShellAppSystem *self)
{
ShellAppSystemPrivate *priv;
GConfClient *client;
self->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
SHELL_TYPE_APP_SYSTEM,
ShellAppSystemPrivate);
priv->cached_favorites = g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify)g_free,
NULL);
priv->apps_tree = gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_NONE);
priv->settings_tree = gmenu_tree_lookup ("settings.menu", GMENU_TREE_FLAGS_NONE);
@ -90,6 +116,12 @@ shell_app_system_init (ShellAppSystem *self)
gmenu_tree_add_monitor (priv->settings_tree, on_tree_changed, self);
reread_menus (self);
client = gconf_client_get_default ();
self->priv->app_monitor_id = gconf_client_notify_add (client, SHELL_APP_FAVORITES_KEY,
on_favorite_apps_changed, self, NULL, NULL);
reread_favorite_apps (self);
}
static void
@ -112,13 +144,16 @@ shell_app_system_finalize (GObject *object)
g_slist_free (priv->cached_setting_ids);
priv->cached_setting_ids = NULL;
g_hash_table_destroy (priv->cached_favorites);
gconf_client_notify_remove (gconf_client_get_default (), priv->app_monitor_id);
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
}
static void
reread_directories (ShellAppSystem *self, GSList **cache, GMenuTree *tree)
{
ShellAppSystemPrivate *priv = self->priv;
GMenuTreeDirectory *trunk;
GSList *entries;
GSList *iter;
@ -228,11 +263,58 @@ on_tree_changed (GMenuTree *monitor, gpointer user_data)
{
ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
g_signal_emit (self, signals[CHANGED], 0);
g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
reread_menus (self);
}
static void
copy_gconf_value_string_list_to_hashset (GConfValue *value,
GHashTable *dest)
{
GSList *list;
GSList *tmp;
list = gconf_value_get_list (value);
for (tmp = list ; tmp; tmp = tmp->next)
{
GConfValue *value = tmp->data;
char *str = g_strdup (gconf_value_get_string (value));
if (!str)
continue;
g_hash_table_insert (dest, str, GUINT_TO_POINTER(1));
}
}
static void
reread_favorite_apps (ShellAppSystem *system)
{
GConfClient *client = gconf_client_get_default ();
GConfValue *val;
val = gconf_client_get (client, SHELL_APP_FAVORITES_KEY, NULL);
if (!(val && val->type == GCONF_VALUE_LIST && gconf_value_get_list_type (val) == GCONF_VALUE_STRING))
return;
g_hash_table_remove_all (system->priv->cached_favorites);
copy_gconf_value_string_list_to_hashset (val, system->priv->cached_favorites);
gconf_value_free (val);
}
void
on_favorite_apps_changed (GConfClient *client,
guint id,
GConfEntry *entry,
gpointer user_data)
{
ShellAppSystem *system = SHELL_APP_SYSTEM (user_data);
reread_favorite_apps (system);
g_signal_emit (G_OBJECT (system), signals[FAVORITES_CHANGED], 0);
}
GType
shell_app_menu_entry_get_type (void)
{
@ -315,3 +397,160 @@ shell_app_system_get_default ()
return instance;
}
/**
* shell_app_system_get_favorites:
*
* Return the list of applications which have been explicitly added to the
* favorites.
*
* Return value: (transfer container) (element-type utf8): List of favorite application ids
*/
GList *
shell_app_system_get_favorites (ShellAppSystem *system)
{
return g_hash_table_get_keys (system->priv->cached_favorites);
}
static void
set_gconf_value_string_list (GConfValue *val, GList *items)
{
GList *iter;
GSList *tmp = NULL;
for (iter = items; iter; iter = iter->next)
{
const char *str = iter->data;
GConfValue *strval = gconf_value_new (GCONF_VALUE_STRING);
gconf_value_set_string (strval, str);
tmp = g_slist_prepend (tmp, strval);
}
tmp = g_slist_reverse (tmp);
gconf_value_set_list (val, tmp);
g_slist_free (tmp);
}
void
shell_app_system_add_favorite (ShellAppSystem *system, const char *id)
{
GConfClient *client = gconf_client_get_default ();
GConfValue *val;
GList *favorites;
val = gconf_value_new (GCONF_VALUE_LIST);
gconf_value_set_list_type (val, GCONF_VALUE_STRING);
g_hash_table_insert (system->priv->cached_favorites, g_strdup (id), GUINT_TO_POINTER (1));
favorites = g_hash_table_get_keys (system->priv->cached_favorites);
set_gconf_value_string_list (val, favorites);
g_list_free (favorites);
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
}
void
shell_app_system_remove_favorite (ShellAppSystem *system, const char *id)
{
GConfClient *client = gconf_client_get_default ();
GConfValue *val;
GList *favorites;
if (!g_hash_table_remove (system->priv->cached_favorites, id))
return;
val = gconf_value_new (GCONF_VALUE_LIST);
gconf_value_set_list_type (val, GCONF_VALUE_STRING);
favorites = g_hash_table_get_keys (system->priv->cached_favorites);
set_gconf_value_string_list (val, favorites);
g_list_free (favorites);
gconf_client_set (client, SHELL_APP_FAVORITES_KEY, val, NULL);
}
static gboolean
desktop_id_exists (ShellAppSystem *system,
const char *target_id,
GMenuTreeDirectory *root)
{
gboolean found = FALSE;
GSList *contents, *iter;
contents = gmenu_tree_directory_get_contents (root);
for (iter = contents; iter; iter = iter->next)
{
GMenuTreeItem *item = iter->data;
if (found)
break;
switch (gmenu_tree_item_get_type (item))
{
case GMENU_TREE_ITEM_ENTRY:
{
GMenuTreeEntry *entry = (GMenuTreeEntry *)item;
const char *id = gmenu_tree_entry_get_desktop_file_id (entry);
if (strcmp (id, target_id) == 0)
found = TRUE;
}
break;
case GMENU_TREE_ITEM_DIRECTORY:
{
GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
found = desktop_id_exists (system, target_id, dir);
}
break;
default:
break;
}
gmenu_tree_item_unref (item);
}
g_slist_free (contents);
return found;
}
/**
* shell_app_system_lookup_basename:
* @name: Probable application identifier
*
* Determine whether a valid .desktop file ID corresponding to a given
* heuristically determined application identifier
* string.
*/
char *
shell_app_system_lookup_basename (ShellAppSystem *system,
const char *name)
{
GMenuTreeDirectory *root;
char *result;
root = gmenu_tree_get_directory_from_path (system->priv->apps_tree, "/");
g_assert (root != NULL);
if (desktop_id_exists (system, name, root))
{
result = g_strdup (name);
goto out;
}
/* These are common "vendor prefixes". But using
* WM_CLASS as a source, we don't get the vendor
* prefix. So try stripping them.
*/
result = g_strjoin ("", "gnome-", name, NULL);
if (desktop_id_exists (system, result, root))
goto out;
result = g_strjoin ("", "fedora-", name, NULL);
if (desktop_id_exists (system, result, root))
goto out;
out:
gmenu_tree_item_unref (root);
return result;
}

View File

@ -25,7 +25,8 @@ struct _ShellAppSystemClass
{
GObjectClass parent_class;
void (*changed)(ShellAppSystem *appsys, gpointer data);
void (*installed_changed)(ShellAppSystem *appsys, gpointer user_data);
void (*favorites_changed)(ShellAppSystem *appsys, gpointer user_data);
};
GType shell_app_system_get_type (void) G_GNUC_CONST;
@ -43,8 +44,16 @@ struct _ShellAppMenuEntry {
GType shell_app_menu_entry_get_type (void);
char * shell_app_system_lookup_basename (ShellAppSystem *system, const char *id);
GSList *shell_app_system_get_menus (ShellAppSystem *system);
GSList *shell_app_system_get_all_settings (ShellAppSystem *system);
GList *shell_app_system_get_favorites (ShellAppSystem *system);
void shell_app_system_add_favorite (ShellAppSystem *system, const char *id);
void shell_app_system_remove_favorite (ShellAppSystem *system, const char *id);
#endif /* __SHELL_APP_SYSTEM_H__ */

View File

@ -101,7 +101,7 @@ shell_global_get_property(GObject *object,
g_value_set_object (value, mutter_plugin_get_overlay_group (global->plugin));
break;
case PROP_SCREEN:
g_value_set_object (value, mutter_plugin_get_screen (global->plugin));
g_value_set_object (value, shell_global_get_screen (global));
break;
case PROP_SCREEN_WIDTH:
{
@ -608,6 +608,17 @@ shell_global_set_stage_input_region (ShellGlobal *global,
shell_global_set_stage_input_mode (global, global->input_mode);
}
/**
* shell_global_get_screen:
*
* Return value: (transfer none): The default #MetaScreen
*/
MetaScreen *
shell_global_get_screen (ShellGlobal *global)
{
return mutter_plugin_get_screen (global->plugin);
}
/**
* shell_global_get_windows:
*
@ -933,6 +944,55 @@ shell_global_create_vertical_gradient (ClutterColor *top,
return texture;
}
/**
* shell_global_create_horizontal_gradient:
* @left: the color on the left
* @right: the color on the right
*
* Creates a horizontal gradient actor.
*
* Return value: (transfer none): a #ClutterCairoTexture actor with the
* gradient. The texture actor is floating, hence (transfer none).
*/
ClutterCairoTexture *
shell_global_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right)
{
ClutterCairoTexture *texture;
cairo_t *cr;
cairo_pattern_t *pattern;
/* Draw the gradient on an 8x1 pixel texture. Because the gradient is drawn
* from the left to the right column, after stretching 1/16 of the
* texture width has the left side color and 1/16 has the right side color.
* There is no reason to use the 8 pixel height that would be similar to the
* reason we are using the 8 pixel width for the vertical gradient, so we
* are just using the 1 pixel height instead.
*/
texture = CLUTTER_CAIRO_TEXTURE (clutter_cairo_texture_new (8, 1));
cr = clutter_cairo_texture_create (texture);
pattern = cairo_pattern_create_linear (0, 0, 8, 0);
cairo_pattern_add_color_stop_rgba (pattern, 0,
left->red / 255.,
left->green / 255.,
left->blue / 255.,
left->alpha / 255.);
cairo_pattern_add_color_stop_rgba (pattern, 1,
right->red / 255.,
right->green / 255.,
right->blue / 255.,
right->alpha / 255.);
cairo_set_source (cr, pattern);
cairo_paint (cr);
cairo_pattern_destroy (pattern);
cairo_destroy (cr);
return texture;
}
/*
* Updates the global->root_pixmap actor with the root window's pixmap or fails
* with a warning.

View File

@ -48,6 +48,8 @@ ClutterActor *shell_get_event_related(ClutterEvent *event);
ShellGlobal *shell_global_get (void);
MetaScreen *shell_global_get_screen (ShellGlobal *global);
void shell_global_grab_dbus_service (ShellGlobal *global);
void shell_global_start_task_panel (ShellGlobal *global);
@ -76,6 +78,9 @@ void shell_global_reexec_self (ShellGlobal *global);
ClutterCairoTexture *shell_global_create_vertical_gradient (ClutterColor *top,
ClutterColor *bottom);
ClutterCairoTexture *shell_global_create_horizontal_gradient (ClutterColor *left,
ClutterColor *right);
ClutterActor *shell_global_create_root_pixmap_actor (ShellGlobal *global);
void shell_global_clutter_cairo_texture_draw_clock (ClutterCairoTexture *texture,