void expose(dt_view_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery) { const int i = dt_conf_get_int("plugins/lighttable/layout"); const double start = dt_get_wtime(); // Let's show full preview if in that state... dt_library_t *lib = (dt_library_t *)self->data; int32_t mouse_over_id; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); if( lib->full_preview_id!=-1 ) { lib->image_over = DT_VIEW_DESERT; cairo_set_source_rgb (cr, .1, .1, .1); cairo_paint(cr); dt_view_image_expose(&(lib->image_over),mouse_over_id, cr, width, height, 1, pointerx, pointery); } else // we do pass on expose to manager or zoomable { switch(i) { case 1: // file manager expose_filemanager(self, cr, width, height, pointerx, pointery); break; default: // zoomable expose_zoomable(self, cr, width, height, pointerx, pointery); break; } } const double end = dt_get_wtime(); dt_print(DT_DEBUG_PERF, "[lighttable] expose took %0.04f sec\n", end-start); }
static gboolean _lib_duplicate_thumb_draw_callback (GtkWidget *widget, cairo_t *cr, dt_lib_module_t *self) { guint width, height; width = gtk_widget_get_allocated_width (widget); height = gtk_widget_get_allocated_height (widget); cairo_set_source_rgb(cr, .2, .2, .2); cairo_paint(cr); int imgid = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),"imgid")); dt_view_image_over_t image_over = DT_VIEW_DESERT; dt_view_image_expose(&image_over, imgid, cr, width, height, 5, 0, 0, FALSE, FALSE); return FALSE; }
void gui_post_expose(dt_lib_module_t *self, cairo_t *cri, int32_t width, int32_t height, int32_t pointerx, int32_t pointery) { dt_lib_duplicate_t *d = (dt_lib_duplicate_t *)self->data; if (d->imgid == 0) return; const int32_t tb = DT_PIXEL_APPLY_DPI(dt_conf_get_int("plugins/darkroom/ui/border_size")); int nw = width-2*tb; int nh = height-2*tb; dt_mipmap_buffer_t buf; dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size(darktable.mipmap_cache, nw, nh); dt_mipmap_cache_get(darktable.mipmap_cache, &buf, d->imgid, mip, DT_MIPMAP_BEST_EFFORT, 'r'); int img_wd = buf.width; int img_ht = buf.height; dt_mipmap_cache_release(darktable.mipmap_cache, &buf); // and now we get the values to "fit the screen" int px,py,nimgw,nimgh; if (img_ht*nw > img_wd*nh) { nimgh = nh; nimgw = img_wd*nh/img_ht; py=0; px=(nw-nimgw)/2; } else { nimgw = nw; nimgh = img_ht*nw/img_wd; px=0; py=(nh-nimgh)/2; } // we erase everything cairo_set_source_rgb(cri, .2, .2, .2); cairo_paint(cri); //we draw the cached image dt_view_image_over_t image_over = DT_VIEW_DESERT; dt_view_image_expose(&image_over, d->imgid, cri, nw, nh, 1, px+tb, py+tb, TRUE, TRUE); //and the nice border line cairo_rectangle(cri, tb+px, tb+py, nimgw, nimgh); cairo_set_line_width(cri, 1.0); cairo_set_source_rgb(cri, .3, .3, .3); cairo_stroke(cri); }
/* store current filmroll */ dt_conf_set_int("plugins/capture/current_filmroll",cv->film->id); } dt_control_log(_("new session initiated '%s'"),cv->jobcode,cv->film->id); } } const char *_capture_view_get_jobcode(const dt_view_t *view) { g_assert( view != NULL ); dt_capture_t *cv=(dt_capture_t *)view->data; return cv->jobcode; } void configure(dt_view_t *self, int wd, int ht) { //dt_capture_t *lib=(dt_capture_t*)self->data; } #define MARGIN 20 #define BAR_HEIGHT 18 /* see libs/camera.c */ void _expose_tethered_mode(dt_view_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery) { dt_capture_t *lib=(dt_capture_t*)self->data; dt_camera_t *cam = (dt_camera_t*)darktable.camctl->active_camera; lib->image_over = DT_VIEW_DESERT; lib->image_id = dt_view_filmstrip_get_activated_imgid(darktable.view_manager); if( cam->is_live_viewing == TRUE) // display the preview { dt_pthread_mutex_lock(&cam->live_view_pixbuf_mutex); if(GDK_IS_PIXBUF(cam->live_view_pixbuf)) { gint pw = gdk_pixbuf_get_width(cam->live_view_pixbuf); gint ph = gdk_pixbuf_get_height(cam->live_view_pixbuf); float w = width-(MARGIN*2.0f); float h = height-(MARGIN*2.0f)-BAR_HEIGHT; float scale; if(cam->live_view_rotation%2 == 0) scale = fminf(w/pw, h/ph); else scale = fminf(w/ph, h/pw); scale = fminf(1.0, scale); cairo_translate(cr, width*0.5, (height+BAR_HEIGHT)*0.5); // origin to middle of canvas if(cam->live_view_flip == TRUE) cairo_scale(cr, -1.0, 1.0); // mirror image cairo_rotate(cr, -M_PI_2*cam->live_view_rotation); // rotate around middle if(cam->live_view_zoom == FALSE) cairo_scale(cr, scale, scale); // scale to fit canvas cairo_translate (cr, -0.5*pw, -0.5*ph); // origin back to corner gdk_cairo_set_source_pixbuf(cr, cam->live_view_pixbuf, 0, 0); cairo_paint(cr); } dt_pthread_mutex_unlock(&cam->live_view_pixbuf_mutex); } else if( lib->image_id >= 0 ) // First of all draw image if availble { cairo_translate(cr,MARGIN, MARGIN); dt_view_image_expose(&(lib->image_over), lib->image_id, cr, width-(MARGIN*2.0f), height-(MARGIN*2.0f), 1, pointerx, pointery, FALSE); } }
static gboolean _lib_filmstrip_expose_callback(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { dt_lib_module_t *self = (dt_lib_module_t *)user_data; dt_lib_filmstrip_t *strip = (dt_lib_filmstrip_t *)self->data; GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); int32_t width = allocation.width; int32_t height = allocation.height; gdouble pointerx = strip->pointerx; gdouble pointery = strip->pointery; if(darktable.gui->center_tooltip == 1) darktable.gui->center_tooltip++; strip->image_over = DT_VIEW_DESERT; DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); /* create cairo surface */ cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget)); /* fill background */ cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); int offset = strip->offset; const float wd = height; const float ht = height; int max_cols = (int)(width/(float)wd) + 2; if (max_cols%2 == 0) max_cols += 1; const int col_start = max_cols/2 - strip->offset; const int empty_edge = (width - (max_cols * wd))/2; int step_res = SQLITE_ROW; sqlite3_stmt *stmt = NULL; /* mouse over image position in filmstrip */ pointerx -= empty_edge; const int seli = (pointery > 0 && pointery <= ht) ? pointerx / (float)wd : -1; const int img_pointerx = (int)fmodf(pointerx, wd); const int img_pointery = (int)pointery; /* get the count of current collection */ strip->collection_count = dt_collection_get_count (darktable.collection); /* get the collection query */ const gchar *query=dt_collection_get_query (darktable.collection); if(!query) return FALSE; if(offset < 0) strip->offset = offset = 0; if(offset > strip->collection_count-1) strip->offset = offset = strip->collection_count-1; // dt_view_set_scrollbar(self, offset, count, max_cols, 0, 1, 1); DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL); DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, offset - max_cols/2); DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, max_cols); cairo_save(cr); cairo_translate(cr, empty_edge, 0.0f); for(int col = 0; col < max_cols; col++) { if(col < col_start) { cairo_translate(cr, wd, 0.0f); continue; } if(step_res != SQLITE_DONE) { step_res = sqlite3_step(stmt); } if(step_res == SQLITE_ROW) { int id = sqlite3_column_int(stmt, 0); // set mouse over id if(seli == col) { strip->mouse_over_id = id; DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, strip->mouse_over_id); } cairo_save(cr); // FIXME find out where the y translation is done, how big the value is and use it directly instead of getting it from the matrix ... cairo_matrix_t m; cairo_get_matrix(cr, &m); dt_view_image_expose(&(strip->image_over), id, cr, wd, ht, max_cols, img_pointerx, img_pointery, FALSE); cairo_restore(cr); } else if (step_res == SQLITE_DONE) { /* do nothing, just add some empty thumb frames */ } else goto failure; cairo_translate(cr, wd, 0.0f); } failure: cairo_restore(cr); sqlite3_finalize(stmt); if(darktable.gui->center_tooltip == 1) // set in this round { char* tooltip = dt_history_get_items_as_string(strip->mouse_over_id); if(tooltip != NULL) { g_object_set(G_OBJECT(strip->filmstrip), "tooltip-text", tooltip, (char *)NULL); g_free(tooltip); } } else if(darktable.gui->center_tooltip == 2) // not set in this round { darktable.gui->center_tooltip = 0; g_object_set(G_OBJECT(strip->filmstrip), "tooltip-text", "", (char *)NULL); } #ifdef _DEBUG if(darktable.unmuted & DT_DEBUG_CACHE) dt_mipmap_cache_print(darktable.mipmap_cache); #endif /* cleanup */ cairo_destroy(cr); return TRUE; }
static void expose_zoomable (dt_view_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery) { dt_library_t *lib = (dt_library_t *)self->data; float zoom, zoom_x, zoom_y; int32_t mouse_over_id, pan, track, center; /* query new collection count */ lib->collection_count = dt_collection_get_count (darktable.collection); DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); zoom = dt_conf_get_int("plugins/lighttable/images_in_row"); zoom_x = lib->zoom_x; zoom_y = lib->zoom_y; pan = lib->pan; center = lib->center; track = lib->track; lib->image_over = DT_VIEW_DESERT; cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); const float wd = width/zoom; const float ht = width/zoom; static int oldpan = 0; static float oldzoom = -1; if(oldzoom < 0) oldzoom = zoom; // TODO: exaggerate mouse gestures to pan when zoom == 1 if(pan)// && mouse_over_id >= 0) { zoom_x = lib->select_offset_x - /* (zoom == 1 ? 2. : 1.)*/pointerx; zoom_y = lib->select_offset_y - /* (zoom == 1 ? 2. : 1.)*/pointery; } if(!lib->statements.main_query) return; if (track == 0); else if(track > 1) zoom_y += ht; else if(track > 0) zoom_x += wd; else if(track > -2) zoom_x -= wd; else zoom_y -= ht; if(zoom > DT_LIBRARY_MAX_ZOOM) { // double speed. if (track == 0); else if(track > 1) zoom_y += ht; else if(track > 0) zoom_x += wd; else if(track > -2) zoom_x -= wd; else zoom_y -= ht; if(zoom > 1.5*DT_LIBRARY_MAX_ZOOM) { // quad speed. if (track == 0); else if(track > 1) zoom_y += ht; else if(track > 0) zoom_x += wd; else if(track > -2) zoom_x -= wd; else zoom_y -= ht; } } if(oldzoom != zoom) { float oldx = (pointerx + zoom_x)*oldzoom/width; float oldy = (pointery + zoom_y)*oldzoom/width; if(zoom == 1) { zoom_x = (int)oldx*wd; zoom_y = (int)oldy*ht; lib->offset = 0x7fffffff; } else { zoom_x = oldx*wd - pointerx; zoom_y = oldy*ht - pointery; } } oldzoom = zoom; // TODO: replace this with center on top of selected/developed image if(center) { if(mouse_over_id >= 0) { zoom_x = wd*((int)(zoom_x)/(int)wd); zoom_y = ht*((int)(zoom_y)/(int)ht); } else zoom_x = zoom_y = 0.0; center = 0; } // mouse left the area, but we leave mouse over as it was, especially during panning // if(!pan && pointerx > 0 && pointerx < width && pointery > 0 && pointery < height) DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); if(!pan && zoom != 1) DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, -1); // set scrollbar positions, clamp zoom positions if(lib->collection_count == 0) { zoom_x = zoom_y = 0.0f; } else if(zoom < 1.01) { if(zoom_x < 0) zoom_x = 0; if(zoom_x > wd*DT_LIBRARY_MAX_ZOOM-wd) zoom_x = wd*DT_LIBRARY_MAX_ZOOM-wd; if(zoom_y < 0) zoom_y = 0; if(zoom_y > ht*lib->collection_count/MIN(DT_LIBRARY_MAX_ZOOM, zoom)-ht) zoom_y = ht*lib->collection_count/MIN(DT_LIBRARY_MAX_ZOOM, zoom)-ht; } else { if(zoom_x < -wd*DT_LIBRARY_MAX_ZOOM/2) zoom_x = -wd*DT_LIBRARY_MAX_ZOOM/2; if(zoom_x > wd*DT_LIBRARY_MAX_ZOOM-wd) zoom_x = wd*DT_LIBRARY_MAX_ZOOM-wd; if(zoom_y < -height+ht) zoom_y = -height+ht; if(zoom_y > ht*lib->collection_count/MIN(DT_LIBRARY_MAX_ZOOM, zoom)-ht) zoom_y = ht*lib->collection_count/MIN(DT_LIBRARY_MAX_ZOOM, zoom)-ht; } int offset_i = (int)(zoom_x/wd); int offset_j = (int)(zoom_y/ht); if(lib->first_visible_filemanager >= 0) { offset_i = lib->first_visible_filemanager % DT_LIBRARY_MAX_ZOOM; offset_j = lib->first_visible_filemanager / DT_LIBRARY_MAX_ZOOM; } lib->first_visible_filemanager = -1; lib->first_visible_zoomable = offset_i + DT_LIBRARY_MAX_ZOOM*offset_j; // arbitrary 1000 to avoid bug due to round towards zero using (int) int seli = zoom == 1 ? 0 : ((int)(1000 + (pointerx + zoom_x)/wd) - MAX(offset_i, 0) - 1000); int selj = zoom == 1 ? 0 : ((int)(1000 + (pointery + zoom_y)/ht) - offset_j - 1000); float offset_x = (zoom == 1) ? 0.0 : (zoom_x/wd - (int)(zoom_x/wd)); float offset_y = (zoom == 1) ? 0.0 : (zoom_y/ht - (int)(zoom_y/ht)); const int max_rows = (zoom == 1) ? 1 : (2 + (int)((height)/ht + .5)); const int max_cols = (zoom == 1) ? 1 : (MIN(DT_LIBRARY_MAX_ZOOM - MAX(0, offset_i), 1 + (int)(zoom+.5))); int offset = MAX(0, offset_i) + DT_LIBRARY_MAX_ZOOM*offset_j; int img_pointerx = zoom == 1 ? pointerx : fmodf(pointerx + zoom_x, wd); int img_pointery = zoom == 1 ? pointery : fmodf(pointery + zoom_y, ht); // assure 1:1 is not switching images on resize/tab events: if(!track && lib->offset != 0x7fffffff && zoom == 1) { offset = lib->offset; zoom_x = wd*(offset % DT_LIBRARY_MAX_ZOOM); zoom_y = ht*(offset / DT_LIBRARY_MAX_ZOOM); } else lib->offset = offset; int id, clicked1, last_seli = 1<<30, last_selj = 1<<30; clicked1 = (oldpan == 0 && pan == 1 && lib->button == 1); dt_view_set_scrollbar(self, MAX(0, offset_i), DT_LIBRARY_MAX_ZOOM, zoom, DT_LIBRARY_MAX_ZOOM*offset_j, lib->collection_count, DT_LIBRARY_MAX_ZOOM*max_cols); cairo_translate(cr, -offset_x*wd, -offset_y*ht); cairo_translate(cr, -MIN(offset_i*wd, 0.0), 0.0); for(int row = 0; row < max_rows; row++) { if(offset < 0) { cairo_translate(cr, 0, ht); offset += DT_LIBRARY_MAX_ZOOM; continue; } /* clear and reset main query */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.main_query); DT_DEBUG_SQLITE3_RESET(lib->statements.main_query); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 1, offset); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 2, max_cols); for(int col = 0; col < max_cols; col++) { if(sqlite3_step(lib->statements.main_query) == SQLITE_ROW) { id = sqlite3_column_int(lib->statements.main_query, 0); // set mouse over id if((zoom == 1 && mouse_over_id < 0) || ((!pan || track) && seli == col && selj == row)) { mouse_over_id = id; DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, mouse_over_id); } // add clicked image to selected table if(clicked1) { if((lib->modifiers & GDK_SHIFT_MASK) == 0 && (lib->modifiers & GDK_CONTROL_MASK) == 0 && seli == col && selj == row) { /* clear selection except id */ /* clear and resest statement */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.delete_except_arg); DT_DEBUG_SQLITE3_RESET(lib->statements.delete_except_arg); /* reuse statment */ DT_DEBUG_SQLITE3_BIND_INT(lib->statements.delete_except_arg, 1, id); sqlite3_step(lib->statements.delete_except_arg); } // FIXME: whatever comes first assumtion is broken! // if((lib->modifiers & GDK_SHIFT_MASK) && (last_seli == (1<<30)) && // (image->id == lib->last_selected_id || image->id == mouse_over_id)) { last_seli = col; last_selj = row; } // if(last_seli < (1<<30) && ((lib->modifiers & GDK_SHIFT_MASK) && (col >= MIN(last_seli,seli) && row >= MIN(last_selj,selj) && // col <= MAX(last_seli,seli) && row <= MAX(last_selj,selj)) && (col != last_seli || row != last_selj)) || if((lib->modifiers & GDK_SHIFT_MASK) && id == lib->last_selected_idx) { last_seli = col; last_selj = row; } if((last_seli < (1<<30) && ((lib->modifiers & GDK_SHIFT_MASK) && (col >= last_seli && row >= last_selj && col <= seli && row <= selj) && (col != last_seli || row != last_selj))) || (seli == col && selj == row)) { // insert all in range if shift, or only the one the mouse is over for ctrl or plain click. dt_view_toggle_selection(id); lib->last_selected_idx = id; } } cairo_save(cr); // if(zoom == 1) dt_image_prefetch(image, DT_IMAGE_MIPF); dt_view_image_expose(&(lib->image_over), id, cr, wd, zoom == 1 ? height : ht, zoom, img_pointerx, img_pointery); cairo_restore(cr); } else goto failure; cairo_translate(cr, wd, 0.0f); } cairo_translate(cr, -max_cols*wd, ht); offset += DT_LIBRARY_MAX_ZOOM; } failure: oldpan = pan; lib->zoom_x = zoom_x; lib->zoom_y = zoom_y; lib->track = 0; lib->center = center; if(darktable.unmuted & DT_DEBUG_CACHE) dt_mipmap_cache_print(darktable.mipmap_cache); }
static void expose_filemanager (dt_view_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery) { dt_library_t *lib = (dt_library_t *)self->data; gboolean offset_changed = FALSE; /* query new collection count */ lib->collection_count = dt_collection_get_count (darktable.collection); if(darktable.gui->center_tooltip == 1) darktable.gui->center_tooltip = 2; /* get grid stride */ const int iir = dt_conf_get_int("plugins/lighttable/images_in_row"); /* get image over id */ lib->image_over = DT_VIEW_DESERT; int32_t mouse_over_id, mouse_over_group = -1; DT_CTL_GET_GLOBAL(mouse_over_id, lib_image_mouse_over_id); /* fill background */ cairo_set_source_rgb (cr, .2, .2, .2); cairo_paint(cr); if(lib->first_visible_zoomable >= 0) { lib->offset = lib->first_visible_zoomable; } lib->first_visible_zoomable = -1; /* check if offset has been changed */ if(lib->track > 2) lib->offset += iir; if(lib->track < -2) lib->offset -= iir; lib->track = 0; if(lib->center) lib->offset = 0; lib->center = 0; int offset = lib->offset; /* if offset differs then flag as changed */ if (offset != lib->first_visible_filemanager) offset_changed = TRUE; lib->first_visible_filemanager = offset; static int oldpan = 0; const int pan = lib->pan; const float wd = width/(float)iir; const float ht = width/(float)iir; int pi = pointerx / (float)wd; int pj = pointery / (float)ht; if(pointerx < 0 || pointery < 0) pi = pj = -1; //const int pidx = grid_to_index(pj, pi, iir, offset); const int img_pointerx = iir == 1 ? pointerx : fmodf(pointerx, wd); const int img_pointery = iir == 1 ? pointery : fmodf(pointery, ht); const int max_rows = 1 + (int)((height)/ht + .5); const int max_cols = iir; int id; int clicked1 = (oldpan == 0 && pan == 1 && lib->button == 1); /* get the count of current collection */ if(lib->collection_count == 0) { const float fs = 15.0f; const float ls = 1.5f*fs; const float offy = height*0.2f; const float offx = 60; const float at = 0.3f; cairo_set_font_size(cr, fs); cairo_set_source_rgba(cr, .7, .7, .7, 1.0f); cairo_select_font_face (cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); cairo_move_to(cr, offx, offy); cairo_show_text(cr, _("there are no images in this collection")); cairo_move_to(cr, offx, offy + 2*ls); cairo_show_text(cr, _("if you have not imported any images yet")); cairo_move_to(cr, offx, offy + 3*ls); cairo_show_text(cr, _("you can do so in the import module")); cairo_move_to(cr, offx - 10.0f, offy + 3*ls - ls*.25f); cairo_line_to(cr, 0.0f, 10.0f); cairo_set_source_rgba(cr, .7, .7, .7, at); cairo_stroke(cr); cairo_move_to(cr, offx, offy + 5*ls); cairo_set_source_rgba(cr, .7, .7, .7, 1.0f); cairo_show_text(cr, _("try to relax the filter settings in the top panel")); cairo_rel_move_to(cr, 10.0f, -ls*.25f); cairo_line_to(cr, width*0.5f, 0.0f); cairo_set_source_rgba(cr, .7, .7, .7, at); cairo_stroke(cr); cairo_move_to(cr, offx, offy + 6*ls); cairo_set_source_rgba(cr, .7, .7, .7, 1.0f); cairo_show_text(cr, _("or add images in the collection module in the left panel")); cairo_move_to(cr, offx - 10.0f, offy + 6*ls - ls*0.25f); cairo_rel_line_to(cr, - offx + 10.0f, 0.0f); cairo_set_source_rgba(cr, .7, .7, .7, at); cairo_stroke(cr); return; } /* do we have a main query collection statement */ if(!lib->statements.main_query) return; if(offset < 0) lib->offset = offset = 0; while(offset >= lib->collection_count) lib->offset = (offset -= iir); /* update scroll borders */ dt_view_set_scrollbar(self, 0, 1, 1, offset, lib->collection_count, max_rows*iir); /* let's reset and reuse the main_query statement */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.main_query); DT_DEBUG_SQLITE3_RESET(lib->statements.main_query); /* setup offset and row for the main query */ DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 1, offset); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 2, max_rows*iir); if(mouse_over_id != -1) { const dt_image_t *mouse_over_image = dt_image_cache_read_get(darktable.image_cache, mouse_over_id); mouse_over_group = mouse_over_image->group_id; dt_image_cache_read_release(darktable.image_cache, mouse_over_image); DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.is_grouped); DT_DEBUG_SQLITE3_RESET(lib->statements.is_grouped); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.is_grouped, 1, mouse_over_group); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.is_grouped, 2, mouse_over_id); if(sqlite3_step(lib->statements.is_grouped) != SQLITE_ROW) mouse_over_group = -1; } // prefetch the ids so that we can peek into the future to see if there are adjacent images in the same group. int *query_ids = g_malloc0(max_rows*max_cols*sizeof(int)); for(int row = 0; row < max_rows; row++) { for(int col = 0; col < max_cols; col++) { if(sqlite3_step(lib->statements.main_query) == SQLITE_ROW) query_ids[row*iir+col] = sqlite3_column_int(lib->statements.main_query, 0); else goto end_query_cache; } } end_query_cache: cairo_save(cr); for(int row = 0; row < max_rows; row++) { for(int col = 0; col < max_cols; col++) { //curidx = grid_to_index(row, col, iir, offset); id = query_ids[row*iir+col]; if(id > 0) { if (iir == 1 && row) continue; /* set mouse over id if pointer is in current row / col */ if(pi == col && pj == row) { mouse_over_id = id; DT_CTL_SET_GLOBAL(lib_image_mouse_over_id, mouse_over_id); } /* handle mouse click on current row / col this could easily and preferable be moved to button_pressed() */ if (clicked1 && (pi == col && pj == row)) { if ((lib->modifiers & (GDK_SHIFT_MASK|GDK_CONTROL_MASK)) == 0) dt_selection_select_single(darktable.selection, id); else if ((lib->modifiers & (GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) dt_selection_toggle(darktable.selection, id); else if ((lib->modifiers & (GDK_SHIFT_MASK)) == GDK_SHIFT_MASK) dt_selection_select_range(darktable.selection, id); } cairo_save(cr); // if(iir == 1) dt_image_prefetch(image, DT_IMAGE_MIPF); dt_view_image_expose(&(lib->image_over), id, cr, wd, iir == 1 ? height : ht, iir, img_pointerx, img_pointery); cairo_restore(cr); } else goto failure; cairo_translate(cr, wd, 0.0f); } cairo_translate(cr, -max_cols*wd, ht); } cairo_restore(cr); // and now the group borders for(int row = 0; row < max_rows; row++) { for(int col = 0; col < max_cols; col++) { id = query_ids[row*iir+col]; if(id > 0) { const dt_image_t *image = dt_image_cache_read_get(darktable.image_cache, id); int group_id = -1; if(image) group_id = image->group_id; dt_image_cache_read_release(darktable.image_cache, image); if (iir == 1 && row) continue; cairo_save(cr); gboolean paint_border = FALSE; // regular highlight border if(group_id != -1) { if(mouse_over_group == group_id && iir > 1 && ((!darktable.gui->grouping && dt_conf_get_bool("plugins/lighttable/draw_group_borders")) || group_id == darktable.gui->expanded_group_id)) { cairo_set_source_rgb(cr, 1, 0.8, 0); paint_border = TRUE; } // border of expanded group else if(darktable.gui->grouping && group_id == darktable.gui->expanded_group_id && iir > 1) { cairo_set_source_rgb(cr, 0, 0, 1); paint_border = TRUE; } } if(paint_border) { int neighbour_group = -1; // top border if(row > 0) { int _id = query_ids[(row-1)*iir+col]; if(_id > 0) { const dt_image_t *_img = dt_image_cache_read_get(darktable.image_cache, _id); neighbour_group = _img->group_id; dt_image_cache_read_release(darktable.image_cache, _img); } } if(neighbour_group != group_id) { cairo_move_to(cr, 0, 0); cairo_line_to(cr, wd, 0); } // left border neighbour_group = -1; if(col > 0) { int _id = query_ids[row*iir+(col-1)]; if(_id > 0) { const dt_image_t *_img = dt_image_cache_read_get(darktable.image_cache, _id); neighbour_group = _img->group_id; dt_image_cache_read_release(darktable.image_cache, _img); } } if(neighbour_group != group_id) { cairo_move_to(cr, 0, 0); cairo_line_to(cr, 0, ht); } // bottom border neighbour_group = -1; if(row < max_rows-1) { int _id = query_ids[(row+1)*iir+col]; if(_id > 0) { const dt_image_t *_img = dt_image_cache_read_get(darktable.image_cache, _id); neighbour_group = _img->group_id; dt_image_cache_read_release(darktable.image_cache, _img); } } if(neighbour_group != group_id) { cairo_move_to(cr, 0, ht); cairo_line_to(cr, wd, ht); } // right border neighbour_group = -1; if(col < max_cols-1) { int _id = query_ids[row*iir+(col+1)]; if(_id > 0) { const dt_image_t *_img = dt_image_cache_read_get(darktable.image_cache, _id); neighbour_group = _img->group_id; dt_image_cache_read_release(darktable.image_cache, _img); } } if(neighbour_group != group_id) { cairo_move_to(cr, wd, 0); cairo_line_to(cr, wd, ht); } cairo_set_line_width(cr, 0.01*wd); cairo_stroke(cr); } cairo_restore(cr); } else goto failure; cairo_translate(cr, wd, 0.0f); } cairo_translate(cr, -max_cols*wd, ht); } /* check if offset was changed and we need to prefetch thumbs */ if (offset_changed) { int32_t imgids_num = 0; const int prefetchrows = .5*max_rows+1; int32_t imgids[prefetchrows*iir]; /* clear and reset main query */ DT_DEBUG_SQLITE3_CLEAR_BINDINGS(lib->statements.main_query); DT_DEBUG_SQLITE3_RESET(lib->statements.main_query); /* setup offest and row for prefetch */ DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 1, offset + max_rows*iir); DT_DEBUG_SQLITE3_BIND_INT(lib->statements.main_query, 2, prefetchrows*iir); // prefetch jobs in inverse order: supersede previous jobs: most important last while(sqlite3_step(lib->statements.main_query) == SQLITE_ROW && imgids_num < prefetchrows*iir) imgids[imgids_num++] = sqlite3_column_int(lib->statements.main_query, 0); float imgwd = iir == 1 ? 0.97 : 0.8; dt_mipmap_size_t mip = dt_mipmap_cache_get_matching_size( darktable.mipmap_cache, imgwd*wd, imgwd*(iir==1?height:ht)); while(imgids_num > 0) { imgids_num --; dt_mipmap_buffer_t buf; dt_mipmap_cache_read_get( darktable.mipmap_cache, &buf, imgids[imgids_num], mip, DT_MIPMAP_PREFETCH); } } failure: g_free(query_ids); oldpan = pan; if(darktable.unmuted & DT_DEBUG_CACHE) dt_mipmap_cache_print(darktable.mipmap_cache); if(darktable.gui->center_tooltip == 1) // set in this round { char* tooltip = dt_history_get_items_as_string(mouse_over_id); if(tooltip != NULL) { g_object_set(G_OBJECT(dt_ui_center(darktable.gui->ui)), "tooltip-text", tooltip, (char *)NULL); g_free(tooltip); } } else if(darktable.gui->center_tooltip == 2) // not set in this round { darktable.gui->center_tooltip = 0; g_object_set(G_OBJECT(dt_ui_center(darktable.gui->ui)), "tooltip-text", "", (char *)NULL); } }