Commit 359bf86e authored by Jim Nelson's avatar Jim Nelson

#533: Holding down the arrow key doesn't hang Shotwell any longer. #63: Quick...

#533: Holding down the arrow key doesn't hang Shotwell any longer.  #63: Quick compare holding down the Shift 
key of a modified photo (works both in library and direct-edit mode).  #537: Slideshow now begins with selected 
photo.  #665: Last event now marked with its end time.  #668: Toolbar now remains in-place in fullscreen mode 
when using compiz.
parent 9877aab2
......@@ -67,6 +67,8 @@ public class FullscreenWindow : PageWindow {
toolbar_window.set_border_width(0);
toolbar_window.add(toolbar);
toolbar_window.realize += on_toolbar_realized;
add(current_page);
// need to create a Gdk.Window to set masks
......@@ -133,9 +135,7 @@ public class FullscreenWindow : PageWindow {
return false;
}
private void invoke_toolbar() {
toolbar_window.show_all();
private void on_toolbar_realized() {
Gtk.Requisition req;
toolbar_window.size_request(out req);
......@@ -151,6 +151,10 @@ public class FullscreenWindow : PageWindow {
toolbar_window.move(tx, ty);
toolbar_window.set_opacity(TOOLBAR_OPACITY);
}
private void invoke_toolbar() {
toolbar_window.show_all();
is_toolbar_shown = true;
......
......@@ -885,9 +885,13 @@ public class CollectionPage : CheckerboardPage {
private void on_slideshow() {
if (get_count() == 0)
return;
Thumbnail thumbnail = (Thumbnail) get_fullscreen_photo();
if (thumbnail == null)
return;
AppWindow.get_instance().go_fullscreen(new FullscreenWindow(new SlideshowPage(this,
(Thumbnail) get_first_item())));
thumbnail)));
}
private void on_view_menu() {
......
......@@ -485,6 +485,15 @@ public class LibraryWindow : AppWindow {
last_exposure = exposure_time;
}
// mark the last event's end time
if (current_event_id.is_valid()) {
assert(last_exposure != 0);
event_table.set_end_time(current_event_id, last_exposure);
events_directory_page.add_event(current_event_id);
events_directory_page.refresh();
}
}
private void on_photo_removed(LibraryPhoto photo) {
......
......@@ -1101,7 +1101,7 @@ public abstract class CheckerboardPage : Page {
if (layout.items.size == 0)
return null;
int index = layout.items.index_of(current);
int index = layout.items.locate(current);
// although items may be added while the page is away, not handling situations where an active
// item is removed
......@@ -1118,7 +1118,7 @@ public abstract class CheckerboardPage : Page {
if (layout.items.size == 0)
return null;
int index = layout.items.index_of(current);
int index = layout.items.locate(current);
// although items may be added while the page is away, not handling situations where an active
// item is removed
......@@ -1266,8 +1266,11 @@ public abstract class SinglePhotoPage : Page {
set_event_source(canvas);
}
public void set_default_interp(Gdk.InterpType default_interp) {
public Gdk.InterpType set_default_interp(Gdk.InterpType default_interp) {
Gdk.InterpType old = this.default_interp;
this.default_interp = default_interp;
return old;
}
public void set_pixbuf(Gdk.Pixbuf unscaled) {
......
......@@ -677,6 +677,72 @@ public abstract class TransformablePhoto: PhotoBase {
return (scale == SCREEN) ? get_screen_scale() : scale;
}
// Returns a raw, untransformed, unscaled pixbuf from the source that has been rotated
// according to its original EXIF settings
public Gdk.Pixbuf get_original_pixbuf(int scale, Gdk.InterpType interp = DEFAULT_INTERP) throws Error {
#if MEASURE_PIPELINE
Timer timer = new Timer();
Timer total_timer = new Timer();
double load_and_decode_time = 0.0, pixbuf_copy_time = 0.0, scale_time = 0.0,
orientation_time = 0.0;
total_timer.start();
#endif
Gdk.Pixbuf pixbuf = null;
if (cached_raw != null && cached_photo_id.id == photo_id.id) {
#if MEASURE_PIPELINE
timer.start();
#endif
pixbuf = cached_raw.copy();
#if MEASURE_PIPELINE
pixbuf_copy_time = timer.elapsed();
#endif
} else {
#if MEASURE_PIPELINE
timer.start();
#endif
pixbuf = get_raw_pixbuf();
#if MEASURE_PIPELINE
load_and_decode_time = timer.elapsed();
timer.start();
#endif
cached_raw = pixbuf.copy();
#if MEASURE_PIPELINE
pixbuf_copy_time = timer.elapsed();
#endif
cached_photo_id = photo_id;
}
// scale
#if MEASURE_PIPELINE
timer.start();
#endif
int pixels = scale_to_pixels(scale);
if (pixels > 0)
pixbuf = scale_pixbuf(pixbuf, pixels, interp);
#if MEASURE_PIPELINE
scale_time = timer.elapsed();
#endif
// orientation
#if MEASURE_PIPELINE
timer.start();
#endif
Orientation orientation = photo_table.get_original_orientation(photo_id);
pixbuf = orientation.rotate_pixbuf(pixbuf);
#if MEASURE_PIPELINE
orientation_time = timer.elapsed();
debug("ORIGINAL PIPELINE: load_and_decode=%lf pixbuf_copy=%lf scale=%lf orientation=%lf total=%lf",
load_and_decode_time, pixbuf_copy_time, scale_time, orientation_time, total_timer.elapsed());
#endif
return pixbuf;
}
// A preview pixbuf is one that can be quickly generated and scaled as a preview while the
// fully transformed pixbuf is built. It is fully transformed.
//
......@@ -825,7 +891,7 @@ public abstract class TransformablePhoto: PhotoBase {
#if MEASURE_PIPELINE
double total_time = total_timer.elapsed();
debug("Pipeline: load_and_decode=%lf pixbuf_copy=%lf redeye=%lf crop=%lf scale=%lf adjustment=%lf enhancement=%lf orientation=%lf total=%lf",
debug("PIPELINE: load_and_decode=%lf pixbuf_copy=%lf redeye=%lf crop=%lf scale=%lf adjustment=%lf enhancement=%lf orientation=%lf total=%lf",
load_and_decode_time, pixbuf_copy_time, redeye_time, crop_time, scale_time, adjustment_time,
enhance_time, orientation_time, total_time);
#endif
......
......@@ -25,6 +25,8 @@ public abstract class EditingHostPage : SinglePhotoPage {
private Gtk.Window container = null;
private PhotoCollection controller = null;
private TransformablePhoto photo = null;
private Gdk.Pixbuf original = null;
private Gdk.Pixbuf swapped = null;
private Gtk.Toolbar toolbar = new Gtk.Toolbar();
private Gtk.ToolButton rotate_button = null;
private Gtk.ToggleToolButton crop_button = null;
......@@ -34,10 +36,9 @@ public abstract class EditingHostPage : SinglePhotoPage {
private Gtk.ToolButton prev_button = new Gtk.ToolButton.from_stock(Gtk.STOCK_GO_BACK);
private Gtk.ToolButton next_button = new Gtk.ToolButton.from_stock(Gtk.STOCK_GO_FORWARD);
private EditingTool current_tool = null;
// drag-and-drop state
private File drag_file = null;
private uint32 last_nav_key = 0;
public virtual signal void check_replace_photo(TransformablePhoto old_photo,
TransformablePhoto new_photo, out bool ok) {
ok = true;
......@@ -145,6 +146,8 @@ public abstract class EditingHostPage : SinglePhotoPage {
}
protected void replace_photo(TransformablePhoto new_photo) {
assert(new_photo != photo);
// only check if okay if there's something to replace
if (photo != null) {
bool ok;
......@@ -171,6 +174,10 @@ public abstract class EditingHostPage : SinglePhotoPage {
// signal the photo has been replaced
contents_changed(1);
selection_changed(1);
// clear out the comparison buffers
original = null;
swapped = null;
}
private void quick_update_pixbuf() {
......@@ -182,6 +189,12 @@ public abstract class EditingHostPage : SinglePhotoPage {
private bool update_pixbuf() {
set_pixbuf(photo.get_pixbuf(TransformablePhoto.SCREEN));
// fetch the original for quick comparisons ... want a pixbuf with no transformations
// (except original orientation)
if (original == null)
original = photo.get_original_pixbuf(TransformablePhoto.SCREEN);
return false;
}
......@@ -197,6 +210,34 @@ public abstract class EditingHostPage : SinglePhotoPage {
prev_button.sensitive = multiple;
next_button.sensitive = multiple;
}
private override bool on_shift_pressed(Gdk.EventKey event) {
// show quick compare of original only if no tool is in use, the original pixbuf is handy,
// and using quality interp to avoid pixellation if the user goes crazy with the shift key
if (current_tool == null && original != null) {
// store what's currently displayed only for the duration of the shift pressing
swapped = get_unscaled_pixbuf();
Gdk.InterpType interp = set_default_interp(QUALITY_INTERP);
set_pixbuf(original);
set_default_interp(interp);
}
return base.on_shift_pressed(event);
}
private override bool on_shift_released(Gdk.EventKey event) {
if (current_tool == null && swapped != null) {
Gdk.InterpType interp = set_default_interp(QUALITY_INTERP);
set_pixbuf(swapped);
set_default_interp(interp);
// only store swapped once; it'll be set the next on_shift_pressed
swapped = null;
}
return base.on_shift_pressed(event);
}
private void activate_tool(EditingTool tool) {
// during editing, always use the quality interpolation, so the editing tool is only
......@@ -379,16 +420,27 @@ public abstract class EditingHostPage : SinglePhotoPage {
return true;
}
// if the user holds the arrow keys down, we will receive a steady stream of key press
// events for an operation that isn't designed for a rapid succession of output ...
// we staunch the supply of new photos to once a second (#533)
bool nav_ok = (event.time - last_nav_key) > 1000;
bool handled = true;
switch (Gdk.keyval_name(event.keyval)) {
case "Left":
case "KP_Left":
on_previous_photo();
if (nav_ok)
on_previous_photo();
else
handled = false;
break;
case "Right":
case "KP_Right":
on_next_photo();
if (nav_ok)
on_next_photo();
else
handled = false;
break;
default:
......@@ -396,9 +448,12 @@ public abstract class EditingHostPage : SinglePhotoPage {
break;
}
if (handled)
return true;
if (handled) {
last_nav_key = event.time;
return true;
}
return (base.key_press_event != null) ? base.key_press_event(event) : true;
}
......
......@@ -71,6 +71,10 @@ public class SortedList<G> : Object, Gee.Iterable<G> {
list.set(index, item);
}
// index_of uses the Comparator to find the item being searched for. Because SortedList allows
// for items identified as equal by the Comparator to co-exist in the list, this method will
// return the first item found where its compare() method returns zero. Use locate() if a
// specific EqualFunc is required for searching.
public int index_of(G search) {
// because the internal ArrayList has no equal_func (and can't easily provide one without
// asking the user for a separate static comparator), search manually here
......@@ -87,6 +91,19 @@ public class SortedList<G> : Object, Gee.Iterable<G> {
return -1;
}
// See notes at index_of for the difference between this method and it.
public int locate(G search, EqualFunc equal_func = direct_equal) {
int index = 0;
foreach (G item in list) {
if (equal_func(item, search))
return index;
index++;
}
return -1;
}
public void insert(int index, G item) {
list.insert(index, item);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment