URI: 
       grid.c - ltkx - GUI toolkit for X11 (old)
  HTML git clone git://lumidify.org/ltkx.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/ltkx.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltkx.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       grid.c (8959B)
       ---
            1 /*
            2  * This file is part of the Lumidify ToolKit (LTK)
            3  * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
            4  *
            5  * Permission is hereby granted, free of charge, to any person obtaining a copy
            6  * of this software and associated documentation files (the "Software"), to deal
            7  * in the Software without restriction, including without limitation the rights
            8  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
            9  * copies of the Software, and to permit persons to whom the Software is
           10  * furnished to do so, subject to the following conditions:
           11  *
           12  * The above copyright notice and this permission notice shall be included in all
           13  * copies or substantial portions of the Software.
           14  *
           15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
           16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
           17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
           18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
           19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
           20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
           21  * SOFTWARE.
           22  */
           23 
           24 /* TODO: remove_widget function that also adjusts static width */
           25 
           26 #include <X11/Xlib.h>
           27 #include <X11/Xutil.h>
           28 #include "khash.h"
           29 #include "ltk.h"
           30 #include "grid.h"
           31 
           32 void ltk_set_row_weight(LtkGrid * grid, int row, int weight)
           33 {
           34         grid->row_weights[row] = weight;
           35         ltk_recalculate_grid(grid);
           36 }
           37 
           38 void ltk_set_column_weight(LtkGrid * grid, int column, int weight)
           39 {
           40         grid->column_weights[column] = weight;
           41         ltk_recalculate_grid(grid);
           42 }
           43 
           44 void ltk_draw_grid(LtkGrid *grid)
           45 {
           46         int i;
           47         for (i = 0; i < grid->rows * grid->columns; i++) {
           48                 if (!grid->widget_grid[i])
           49                         continue;
           50                 LtkWidget *ptr = grid->widget_grid[i];
           51                 ptr->draw(ptr);
           52         }
           53 }
           54 
           55 LtkGrid *ltk_create_grid(LtkWindow *window, int rows, int columns)
           56 {
           57         LtkGrid *grid = malloc(sizeof(LtkGrid));
           58 
           59         ltk_fill_widget_defaults(&grid->widget, window, &ltk_draw_grid, &ltk_destroy_grid, 0);
           60         grid->widget.mouse_press = &ltk_grid_mouse_press;
           61         grid->widget.mouse_release = &ltk_grid_mouse_release;
           62         grid->widget.motion_notify = &ltk_grid_motion_notify;
           63         grid->widget.resize = &ltk_recalculate_grid;
           64 
           65         grid->rows = rows;
           66         grid->columns = columns;
           67         grid->widget_grid = malloc(rows * columns * sizeof(LtkWidget));
           68         grid->row_heights = malloc(rows * sizeof(int));
           69         grid->column_widths = malloc(rows * sizeof(int));
           70         grid->row_weights = malloc(rows * sizeof(int));
           71         grid->column_weights = malloc(columns * sizeof(int));
           72         /* Positions have one extra for the end */
           73         grid->row_pos = malloc((rows + 1) * sizeof(int));
           74         grid->column_pos = malloc((columns + 1) * sizeof(int));
           75         int i;
           76         for (i = 0; i < rows; i++) {
           77                 grid->row_heights[i] = 0;
           78                 grid->row_weights[i] = 0;
           79                 grid->row_pos[i] = 0;
           80         }
           81         grid->row_pos[rows] = 0;
           82         for (i = 0; i < columns; i++) {
           83                 grid->column_widths[i] = 0;
           84                 grid->column_weights[i] = 0;
           85                 grid->column_pos[i] = 0;
           86         }
           87         grid->column_pos[columns] = 0;
           88         for (i = 0; i < rows * columns; i++) {
           89                 grid->widget_grid[i] = NULL;
           90         }
           91 
           92         ltk_recalculate_grid(grid);
           93         return grid;
           94 }
           95 
           96 void ltk_destroy_grid(LtkGrid *grid)
           97 {
           98         LtkWidget *ptr;
           99         int i;
          100         for (i = 0; i < grid->rows * grid->columns; i++) {
          101                 if (grid->widget_grid[i]) {
          102                         ptr = grid->widget_grid[i];
          103                         ptr->destroy(ptr);
          104                 }
          105         }
          106         free(grid->widget_grid);
          107         free(grid->row_heights);
          108         free(grid->column_widths);
          109         free(grid->row_weights);
          110         free(grid->column_weights);
          111         free(grid->row_pos);
          112         free(grid->column_pos);
          113         free(grid);
          114 }
          115 
          116 void ltk_recalculate_grid(LtkGrid *grid)
          117 {
          118         unsigned int height_static = 0, width_static = 0;
          119         unsigned int total_row_weight = 0, total_column_weight = 0;
          120         float height_unit = 0, width_unit = 0;
          121         unsigned int currentx = 0, currenty = 0;
          122         int i, j;
          123         for (i = 0; i < grid->rows; i++) {
          124                 total_row_weight += grid->row_weights[i];
          125                 if (grid->row_weights[i] == 0) {
          126                         height_static += grid->row_heights[i];
          127                 }
          128         }
          129         for (i = 0; i < grid->columns; i++) {
          130                 total_column_weight += grid->column_weights[i];
          131                 if (grid->column_weights[i] == 0) {
          132                         width_static += grid->column_widths[i];
          133                 }
          134         }
          135         if (total_row_weight > 0) {
          136                 height_unit = (float) (grid->widget.rect.h - height_static) / (float) total_row_weight;
          137         }
          138         if (total_column_weight > 0) {
          139                 width_unit = (float) (grid->widget.rect.w - width_static) / (float) total_column_weight;
          140         }
          141         for (i = 0; i < grid->rows; i++) {
          142                 grid->row_pos[i] = currenty;
          143                 if (grid->row_weights[i] > 0) {
          144                         grid->row_heights[i] = grid->row_weights[i] * height_unit;
          145                 }
          146                 currenty += grid->row_heights[i];
          147         }
          148         grid->row_pos[grid->rows] = currenty;
          149         for (i = 0; i < grid->columns; i++) {
          150                 grid->column_pos[i] = currentx;
          151                 if (grid->column_weights[i] > 0) {
          152                         grid->column_widths[i] = grid->column_weights[i] * width_unit;
          153                 }
          154                 currentx += grid->column_widths[i];
          155         }
          156         grid->column_pos[grid->columns] = currentx;
          157         int orig_width, orig_height;
          158         int end_column, end_row;
          159         for (i = 0; i < grid->rows; i++) {
          160                 for (j = 0; j < grid->columns; j++) {
          161                         if (!grid->widget_grid[i * grid->columns + j]) {
          162                                 continue;
          163                         }
          164                         LtkWidget *ptr = grid->widget_grid[i * grid->columns + j];
          165                         orig_width = ptr->rect.w;
          166                         orig_height = ptr->rect.h;
          167                         end_row = i + ptr->row_span;
          168                         end_column = j + ptr->column_span;
          169                         if (ptr->sticky & LTK_STICKY_LEFT && ptr->sticky & LTK_STICKY_RIGHT) {
          170                                 ptr->rect.w = grid->column_pos[end_column] - grid->column_pos[j];
          171                         }
          172                         if (ptr->sticky & LTK_STICKY_TOP && ptr->sticky & LTK_STICKY_BOTTOM) {
          173                                 ptr->rect.h = grid->row_pos[end_row] - grid->row_pos[i];
          174                         }
          175                         if (orig_width != ptr->rect.w || orig_height != ptr->rect.h) {
          176                                 if (ptr->resize) {
          177                                         ptr->resize(ptr, orig_width, orig_height);
          178                                 }
          179                         }
          180 
          181                         if (ptr->sticky & LTK_STICKY_RIGHT) {
          182                                 ptr->rect.x = grid->column_pos[end_column] - ptr->rect.w;
          183                         } else if (ptr->sticky & LTK_STICKY_LEFT) {
          184                                 ptr->rect.x = grid->column_pos[j];
          185                         } else {
          186                                 ptr->rect.x = grid->column_pos[j] + ((grid->column_pos[end_column] - grid->column_pos[j]) / 2 - ptr->rect.w / 2);
          187                         }
          188 
          189                         if (ptr->sticky & LTK_STICKY_BOTTOM) {
          190                                 ptr->rect.y = grid->row_pos[end_row] - ptr->rect.h;
          191                         } else if (ptr->sticky & LTK_STICKY_TOP) {
          192                                 ptr->rect.y = grid->row_pos[i];
          193                         } else {
          194                                 ptr->rect.y = grid->row_pos[i] + ((grid->row_pos[end_row] - grid->row_pos[i]) / 2 - ptr->rect.h / 2);
          195                         }
          196                 }
          197         }
          198 }
          199 
          200 void ltk_grid_widget(LtkWidget *widget, LtkGrid *grid, int row, int column, int row_span, int column_span, unsigned short sticky)
          201 {
          202         widget->sticky = sticky;
          203         widget->row = row;
          204         widget->column = column;
          205         widget->row_span = row_span;
          206         widget->column_span = column_span;
          207         if (grid->column_weights[column] == 0 && widget->rect.w > grid->column_widths[column]) {
          208                 grid->column_widths[column] = widget->rect.w;
          209         }
          210         if (grid->row_weights[row] == 0 && widget->rect.h > grid->row_heights[row]) {
          211                 grid->row_heights[row] = widget->rect.h;
          212         }
          213         grid->widget_grid[widget->row * grid->columns + widget->column] = widget;
          214         widget->parent = grid;
          215         ltk_recalculate_grid(grid);
          216 }
          217 
          218 static int ltk_grid_find_nearest_column(LtkGrid *grid, int x)
          219 {
          220         int i;
          221         for (i = 0; i < grid->columns; i++) {
          222                 if (grid->column_pos[i] <= x && grid->column_pos[i + 1] >= x) {
          223                         return i;
          224                 }
          225         }
          226         return -1;
          227 }
          228 
          229 static int ltk_grid_find_nearest_row(LtkGrid *grid, int y)
          230 {
          231         int i;
          232         for (i = 0; i < grid->rows; i++) {
          233                 if (grid->row_pos[i] <= y && grid->row_pos[i + 1] >= y) {
          234                         return i;
          235                 }
          236         }
          237         return -1;
          238 }
          239 
          240 void ltk_grid_mouse_press(LtkGrid *grid, XEvent event)
          241 {
          242         int x = event.xbutton.x;
          243         int y = event.xbutton.y;
          244         int row = ltk_grid_find_nearest_row(grid, y);
          245         int column = ltk_grid_find_nearest_column(grid, x);
          246         if (row == -1 || column == -1)
          247                 return;
          248         LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
          249         if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
          250                 ltk_widget_mouse_press_event(ptr, event);
          251         }
          252 }
          253 
          254 void ltk_grid_mouse_release(LtkGrid *grid, XEvent event)
          255 {
          256         int x = event.xbutton.x;
          257         int y = event.xbutton.y;
          258         int row = ltk_grid_find_nearest_row(grid, y);
          259         int column = ltk_grid_find_nearest_column(grid, x);
          260         if (row == -1 || column == -1)
          261                 return;
          262         LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
          263         if (ptr) {
          264                 if (ltk_collide_rect(ptr->rect, x, y)) {
          265                         ltk_widget_mouse_release_event(ptr, event);
          266                 } else {
          267                         ltk_remove_hover_widget(grid);
          268                         ltk_change_active_widget_state(grid, LTK_ACTIVE);
          269                 }
          270         }
          271 }
          272 
          273 void ltk_grid_motion_notify(LtkGrid *grid, XEvent event)
          274 {
          275         short pressed = (event.xmotion.state & Button1Mask) == Button1Mask;
          276         if (pressed)
          277                 return;
          278         int x = event.xbutton.x;
          279         int y = event.xbutton.y;
          280         int row = ltk_grid_find_nearest_row(grid, y);
          281         int column = ltk_grid_find_nearest_column(grid, x);
          282         if (row == -1 || column == -1)
          283                 return;
          284         LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
          285         if (ptr) {
          286                 if (ltk_collide_rect(ptr->rect, x, y))
          287                         ltk_widget_motion_notify_event(ptr, event);
          288                 else if (!pressed)
          289                         ltk_remove_hover_widget(grid);
          290         }
          291 }