carousel.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Bing Wallpaper GNOME extension
  2. // Copyright (C) 2017-2025 Michael Carroll
  3. // This extension is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Lesser General Public License as published by
  5. // the Free Software Foundation, either version 3 of the License, or
  6. // (at your option) any later version.
  7. // See the GNU General Public License, version 3 or later for details.
  8. // Based on GNOME shell extension NASA APOD by Elia Argentieri https://github.com/Elinvention/gnome-shell-extension-nasa-apod
  9. import Gtk from 'gi://Gtk';
  10. import GdkPixbuf from 'gi://GdkPixbuf';
  11. import Gio from 'gi://Gio';
  12. import * as Utils from './utils.js';
  13. import {gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
  14. const default_dimensions = [30, 30, 1650, 800]; // TODO: pull from and save dimensions to settings, but perhaps verify that dimensions are ok
  15. const GALLERY_THUMB_WIDTH = 320;
  16. const GALLERY_THUMB_HEIGHT = 180;
  17. export default class Carousel {
  18. constructor(settings, button = null, callbackfunc = null, prefs_flowbox = null, extensionPath = null) {
  19. //create_gallery(widget, settings);
  20. this.settings = settings;
  21. this.button = button;
  22. this.callbackfunc = callbackfunc;
  23. this.flowBox = null;
  24. this.window = null;
  25. this.imageList = Utils.imageListSortByDate(Utils.getImageList(this.settings)).reverse(); // get images and reverse order
  26. this.searchEntry = null;
  27. this.extensionPath = extensionPath
  28. this._log('create carousel...');
  29. this.flowBox = prefs_flowbox;
  30. this.flowBox.insert(this._create_placeholder_item(), -1);
  31. }
  32. _enable_button() {
  33. if (this.button) {
  34. this.button.set_sensitive(state);
  35. }
  36. }
  37. _create_gallery() {
  38. this.imageList.forEach((image) => {
  39. let item = this._create_gallery_item(image);
  40. this.flowBox.insert(item, -1);
  41. });
  42. }
  43. _create_gallery_item(image) {
  44. let buildable = new Gtk.Builder();
  45. // grab appropriate object from UI file
  46. buildable.add_objects_from_file(this.extensionPath + '/ui/carousel4.ui', ["flowBoxChild"]);
  47. // assign variables to the UI objects we've just loaded
  48. let galleryImage = buildable.get_object('galleryImage');
  49. let filename = Utils.imageToFilename(this.settings, image);
  50. let viewButton = buildable.get_object('viewButton');
  51. let applyButton = buildable.get_object('applyButton');
  52. let infoButton = buildable.get_object('infoButton');
  53. let deleteButton = buildable.get_object('deleteButton');
  54. let favButton = buildable.get_object('favButton');
  55. let unfavButton = buildable.get_object('unfavButton');
  56. if (Utils.isFavourite(image)) {
  57. favButton.set_visible(false);
  58. this._log('image is favourited');
  59. }
  60. else {
  61. unfavButton.set_visible(false);
  62. }
  63. try {
  64. this._load_image(galleryImage, filename);
  65. }
  66. catch (e) {
  67. galleryImage.set_from_icon_name('image-missing');
  68. galleryImage.set_icon_size = 2; // Gtk.GTK_ICON_SIZE_LARGE;
  69. this._log('create_gallery_image: '+e);
  70. }
  71. galleryImage.set_tooltip_text(image.copyright);
  72. // set up actions for when a image button is clicked
  73. viewButton.connect('clicked', () => {
  74. Utils.openInSystemViewer(filename);
  75. });
  76. applyButton.connect('clicked', () => {
  77. this.settings.set_string('selected-image', Utils.getImageUrlBase(image));
  78. this._log('gallery selected '+Utils.getImageUrlBase(image));
  79. });
  80. infoButton.connect('clicked', () => {
  81. Utils.openInSystemViewer(image.copyrightlink, false);
  82. this._log('info page link opened '+image.copyrightlink);
  83. });
  84. deleteButton.connect('clicked', (widget) => {
  85. this._log('Delete requested for '+filename);
  86. Utils.deleteImage(filename);
  87. Utils.setImageHiddenStatus(this.settings, image.urlbase, true);
  88. Utils.purgeImages(this.settings); // hide image instead
  89. widget.get_parent().get_parent().set_visible(false); // bit of a hack
  90. if (this.callbackfunc)
  91. this.callbackfunc();
  92. });
  93. // button is unchecked, so we want to make the checked one visible
  94. favButton.connect('clicked', (widget) => {
  95. this._log('favourited '+Utils.getImageUrlBase(image));
  96. widget.set_visible(false);
  97. unfavButton.set_visible(true);
  98. Utils.setImageFavouriteStatus(this.settings, image.urlbase, true);
  99. });
  100. // button is checked, so we want to make the unchecked one visible
  101. unfavButton.connect('clicked', (widget) => {
  102. this._log('unfavourited '+Utils.getImageUrlBase(image));
  103. widget.set_visible(false);
  104. favButton.set_visible(true);
  105. Utils.setImageFavouriteStatus(this.settings, image.urlbase, false);
  106. });
  107. let item = buildable.get_object('flowBoxChild');
  108. return item;
  109. }
  110. _create_placeholder_item() {
  111. let buildable = new Gtk.Builder();
  112. this.flowBox.set_max_children_per_line(1);
  113. // grab appropriate object from UI file
  114. buildable.add_objects_from_file(this.extensionPath + '/ui/carousel4.ui', ["flowBoxPlaceholder"]);
  115. let loadButton = buildable.get_object('loadButton');
  116. loadButton.connect('clicked', (widget) => {
  117. widget.set_label(_("Loading...")); // does this work???
  118. this.flowBox.remove(widget.get_parent());
  119. this.flowBox.set_max_children_per_line(2);
  120. this._create_gallery();
  121. });
  122. let item = buildable.get_object('flowBoxPlaceholder');
  123. return item;
  124. }
  125. _load_image(galleryImage, filename) {
  126. let thumb_path = Utils.getWallpaperDir(this.settings)+'.thumbs/';
  127. let thumb_dir = Gio.file_new_for_path(thumb_path);
  128. let save_thumbs = !this.settings.get_boolean('delete-previous') && this.settings.get_boolean('create-thumbs'); // create thumbs only if not deleting previous and thumbs are enabled
  129. if (!thumb_dir.query_exists(null)) {
  130. thumb_dir.make_directory_with_parents(null);
  131. }
  132. let image_file = Gio.file_new_for_path(filename);
  133. // load gallery image or create new thumbnail if it doesn't
  134. if (!image_file.query_exists(null)){
  135. this._set_blank_image(galleryImage);
  136. }
  137. else {
  138. let image_thumb_path = thumb_path + image_file.get_basename();
  139. let image_thumb = Gio.file_new_for_path(image_thumb_path);
  140. try {
  141. let pixbuf;
  142. // use thumbnail if available
  143. if (image_thumb.query_exists(null)) {
  144. pixbuf = GdkPixbuf.Pixbuf.new_from_file(image_thumb_path);
  145. }
  146. else { // save changed thumbnail significantly speeds up gallery loading, but costs some addtional disk space
  147. pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, GALLERY_THUMB_WIDTH, GALLERY_THUMB_HEIGHT);
  148. if (save_thumbs)
  149. pixbuf.savev(image_thumb_path,'jpeg',['quality'], ['90']);
  150. }
  151. galleryImage.set_pixbuf(pixbuf);
  152. }
  153. catch (e) {
  154. this._set_blank_image(galleryImage);
  155. this._log('create_gallery_image: '+e);
  156. }
  157. }
  158. }
  159. _set_blank_image(galleryImage) {
  160. //galleryImage.set_from_icon_name('image-missing');
  161. //galleryImage.set_icon_size = 2; // Gtk.GTK_ICON_SIZE_LARGE;
  162. }
  163. _log(msg) {
  164. if (this.settings.get_boolean('debug-logging'))
  165. console.log("BingWallpaper extension: " + msg); // disable to keep the noise down in journal
  166. }
  167. };