6

This is the sidebar of my nautilus window. You can see two different icons - one for the USB sticks and one for drives.

How does nautilus determine which gets a hard drive icon and which gets a flash drive one?

3
  • This is a very interesting question, especially since I have a flash drive that shows up with a Smart Media icon... (thngs-images.s3.amazonaws.com/archive/012/153/original/…) Jan 4, 2017 at 16:50
  • The 2 8GB Volumes in my image are flash drives plugged in via a USB hub - I assume that's why they appear as HDDs not as flash drives.
    – Tim
    Jan 4, 2017 at 16:55
  • @AndroidDev It could have been either a bug or Gio simply couldn't determine the icon for the volume, so it made next best guess for removable media. It could also be something about the drive itself or the filesystem on the drive ( if its got NTFS, likely Gio would treat it as a drive rather than removable device ) Mar 7, 2019 at 6:24

1 Answer 1

4

TL;DR: Nautilus uses Gio's GDrive,GVolume, and GVolumeMonitor interfaces to obtain icons corresponding to particular device.

Gio's API, drives and icons

Gio has set of classes which allow applications to read available drives and volumes, and what icons are associated to them (and that's something API already handles). The icons are returned as Gio Icon type, so applications don't particularly have to know where the icon is located, however requesting an icon by name or by full path is always possible. There's two types of icons you will see, "symbolic" and custom, and "symbolic" icons are sort of standard. If one looks through /usr/share/icons they'll find there are lots of icons that end with -symbolic in their name, stored across multiple theme folders (standard Adwaita, Humanity, and Oxygen are good examples) , typically .svg files. In the screenshot in the question, the icon for hard drive is drive-harddisk-symbolic and drive-removable-media-usb. When user switches desktop theme, the application doesn't have to look up full path to the icon - so long as there is drive-harddisk-symbolic icon in the theme folder, the Gio will find it and return to the application. How do I know all this ? I've used those same icons for my own UDisks Indicator (though my approach differs from what Nautilus does).

How Nautilus utilizes Gio's api

From reading Nautilus source code, specifically nautilusgtkplacesview.c code, essential parts are the following:

  1. Nautilus adds drives and volumes (think partitions). There's add_drive function, which uses Gio's g_drive_get_volumes (drive) function to get volumes on specific drive, and passes that information to Nautilus's add_volume() function. Gio's g_volume_monitor_get_volumes() on lines 1139 and g_volume_monitor_get_mounts() 1164 fetches volumes that may not be associated with a drive (such as ftp or network shares), but that same information goes to add_volume() function and add_mount().

  2. Icons are queried for mountpoints and volumes: Inside add_volume() function, Gio's g_volume_get_icon() function fetches the icon of type GIcon. Inside add_mount() Gio's g_mount_get_icon() also of type GIcon. In both cases, icon along with other information are combined into an object of type NAUTILUS_TYPE_GTK_PLACES_VIEW_ROW and passed to insert_row() function

  3. Rows inserted into Places container: The Places menu that you show in the example actually is one of the basic Gtk container types - Gtk Box. Inside that box there's ListBox for subsections - that's why there's separators for all user's folders, drives and volumes, and bookmarks. Nautilus creates an object which extends from Gtk Box type, G_DEFINE_TYPE_WITH_PRIVATE (NautilusGtkPlacesView, nautilus_gtk_places_view, GTK_TYPE_BOX) . The G_DEFINE_TYPE_WITH_PRIVATE is another standard function , and as you can see in Nautilus's definition the last item defines GTK_TYPE_BOX - parent object. In the .c file on line 50, NautilusGtkPlacesViewPrivate structure is defined, which among other things has pointer to ListView widget. That's the actual contents of the object.

    Now, insert_row() function takes instance of that type (NautilusGtkPlacesView *view), connects whole bunch of signals to the row item it received as argument, and using gtk_container_add inserts all the information ( along with the icon ) into the ListBox widget of the Nautilus Places object.

Lengthy explanation and probably looks simpler in a diagram, but also that's all written in C. Let's try to make something ourselves in Python which is by far simpler and easier.

Python example

Here's a simple window that utilizes Gio's Drive interface to create buttons with icons corresponding to the attached drives. This is overly simplistic and needs polish, and inline-comments

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gio,Gtk

class drives_window(Gtk.Window):
    def __init__(self):
        super().__init__(title="Foobar")
        self.volume_monitor = Gio.VolumeMonitor.get()
        # We'll use Gtk.Box to hold all the buttons corresponding to each drive
        # although we haven't connected the buttons to any other function
        # so clicking on the does nothing
        self.box = Gtk.Box()
        for drive in self.volume_monitor.get_connected_drives():
            button = Gtk.Button()
            button.set_label(drive.get_name())  
            button.set_always_show_image(True)
            icon = drive.get_symbolic_icon()
            # buttons need Gtk.Image widget, but .get_symbolic_icon() returns Gio.ThemedIcon
            # so we create new Gtk.Image using .new_From_gicon() function
            # Gtk.IconSize.BUTTON is a constant
            button.set_image(Gtk.Image.new_from_gicon(icon,Gtk.IconSize.BUTTON))
            self.box.pack_start(button,True,True,0)
        # add the box container to this window
        self.add(self.box)

window = drives_window()
window.connect("destroy",Gtk.main_quit)
window.show_all()
Gtk.main()

Simpler approach

Of course nowadays you don't have to reinvent the wheel. Gtk provides PlacesSidebar widget.

#!/usr/bin/env python3
from gi.repository import Gtk,Gio,GLib

w = Gtk.Window()
b1 = Gtk.Box()
p = Gtk.PlacesSidebar()
b1.pack_start(p,True,True,0)
b1.pack_start(Gtk.Button("Hello World"),True,True,0)
b1.pack_start(Gtk.Button("Hello World 2"),True,True,0)

w.add(b1)
w..connect("destroy",Gtk.main_quit)
w.show_all()

Gtk.main()

Sidenote: Nautilus also has a header file nautilus-icon-names.h which defines constants with NAUTILUS_ prefix, such as

#define NAUTILUS_ICON_FILESYSTEM    "drive-harddisk-symbolic"

probably for developer/code consistency, instead of having to rely on looking up actual icon names. The only place where that particular definition is used is in get_icon function, and that function ironically is not used for Places side bar but is used inside pathbar updating function. Go figure,right ? ¯\_(ツ)_/¯


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .