1 /* editor.c
2 * Copyright (C) 2002-2004 Pascal Eberhard <pascal.ebo@netcourrier.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20
21 #include "common.h"
22 #include <string.h>
23 #include <gdk/gdkkeysyms.h>
24
25 static void editor_draw_icon(editor_t *e, GdkPixbuf *pixbuf_view,
26 char *iconame, gint x, gint y,
27 gboolean is_valid, gboolean is_cursor);
28 static void editor_build_interface(editor_t *e);
29
30 static gboolean editor_button_press_event(GtkWidget *widget,
31 GdkEventButton *event,
32 editor_t *e);
33 static gboolean editor_motion_notify_event(GtkWidget *widget,
34 GdkEventMotion *event,
35 editor_t *e);
36 static gboolean editor_is_dblclick(editor_t *e, int x, int y);
37 static gboolean editor_button_release_event(GtkWidget *widget,
38 GdkEventButton *event,
39 editor_t *e);
40 static void editor_arrow_up_clicked(GtkWidget *widget, editor_t *e);
41 static void editor_arrow_down_clicked(GtkWidget *widget, editor_t *e);
42 static void editor_arrow_left_clicked(GtkWidget *widget, editor_t *e);
43 static void editor_arrow_right_clicked(GtkWidget *widget, editor_t *e);
44
45 // ----------------------------------------------------------------------------
editor_new(struct apwalapp_t * apwal)46 editor_t* editor_new(struct apwalapp_t *apwal)
47 {
48 editor_t *editor;
49
50 g_assert(apwal != NULL);
51 g_assert(apwal->editor_frame != NULL);
52
53 editor = (editor_t *)malloc(sizeof(editor_t));
54 g_assert(editor != NULL);
55
56 editor->apwal = apwal;
57 editor->dblclick_timer = NULL;
58
59 editor->drag_in_progress = FALSE;
60
61 editor->xwidth = gdk_screen_width();
62 editor->xheight = gdk_screen_height();
63
64 editor->x = -2;
65 editor->y = -2;
66 editor->width = 10;
67 if (editor->xheight <= 480)
68 editor->height = 3;
69 else
70 editor->height = 6;
71
72 editor_build_interface(editor);
73 editor_refresh(editor);
74
75 return editor;
76 }
77 // ----------------------------------------------------------------------------
editor_draw_icon(editor_t * e,GdkPixbuf * pixbuf_view,char * iconame,gint x,gint y,gboolean is_valid,gboolean is_cursor)78 void editor_draw_icon(editor_t *e, GdkPixbuf *pixbuf_view,
79 char *iconame, gint x, gint y, gboolean is_valid,
80 gboolean is_cursor)
81 {
82 GdkPixbuf *pixbuf_icon;
83 gint width;
84 gint height;
85 g_assert(e != NULL && pixbuf_view != NULL && iconame != NULL);
86 // first check if this icon has to be showed
87 if ((x < e->x) || (x >= (e->x + e->width)))
88 return;
89 if ((y < e->y) || (y >= (e->y + e->height)))
90 return;
91
92 // icon pixbuf
93 pixbuf_icon = gdk_pixbuf_new_from_apwal(iconame, NULL, NULL);
94
95 // if the app has something wrong then change is color to N&B.
96 if (!is_valid && !is_cursor)
97 gdk_pixbuf_saturate_and_pixelate(pixbuf_icon, pixbuf_icon,
98 0.1/*saturation*/,
99 TRUE/*pixelate*/);
100
101 if ((x == e->drag_start_x) && (y == e->drag_start_y))
102 {
103 if ((e->drag_in_progress == TRUE) &&
104 ( ! ((x == e->drag_current_x) && (y == e->drag_current_y)) ) )
105 {
106 if (!is_cursor)
107 gdk_pixbuf_saturate_and_pixelate(pixbuf_icon, pixbuf_icon,
108 0.0/*saturation*/,
109 TRUE/*pixelate*/);
110 if ((e->drag_start_x != e->drag_current_x) ||
111 (e->drag_start_y != e->drag_current_y))
112 {
113 if ((!is_cursor) &&
114 (app_list_at_xy(e->apwal->apps, e->drag_current_x,
115 e->drag_current_y) == NULL))
116 editor_draw_icon(e, pixbuf_view, iconame, e->drag_current_x,
117 e->drag_current_y, TRUE/*valid?*/, FALSE/*cursor?*/);
118 }
119 }
120 }
121
122 // accept icons with size lower that 48x48
123 width = gdk_pixbuf_get_width(pixbuf_icon);
124 height = gdk_pixbuf_get_height(pixbuf_icon);
125 width = width < ICON_WIDTH ? width : ICON_WIDTH;
126 height= height< ICON_HEIGHT ? height: ICON_HEIGHT;
127 /*
128 TRACE("x:%d y:%d w:%d h:%d, "
129 "icon x:%d y:%d w:%d h:%d coord:(%d,%d)",
130 e->x, e->y, e->width, e->height,
131 x, y, width, height,
132 (x - e->x) * ICON_WIDTH, (y - e->y) * ICON_HEIGHT);
133 */
134 // put the icon in the image which has to be displayed
135 gdk_pixbuf_composite(pixbuf_icon/*src*/, pixbuf_view/*dest*/,
136 (x - e->x) * ICON_WIDTH/*dest_x*/,
137 (y - e->y) * ICON_HEIGHT/*dest_y*/,
138 gdk_pixbuf_get_width(pixbuf_icon)/*dest_width*/,
139 gdk_pixbuf_get_height(pixbuf_icon)/*dest_height*/,
140 (x - e->x) * ICON_WIDTH/*offset_x*/,
141 (y - e->y) * ICON_WIDTH/*offset_y*/,
142 1/*scale_x*/,
143 1/*scale_y*/,
144 GDK_INTERP_NEAREST/*interp_type*/,
145 255/*overall_alpha*/);
146 g_object_unref(pixbuf_icon);
147
148 return;
149 }
150 // ----------------------------------------------------------------------------
editor_refresh(editor_t * e)151 void editor_refresh(editor_t *e)
152 {
153 GdkPixbuf *pixbuf_view;
154 app_t *app;
155 gboolean is_valid;
156
157 g_assert(e != NULL);
158 g_assert(e->apwal != NULL);
159 g_assert(e->apwal->apps != NULL);
160
161 pixbuf_view = gdk_pixbuf_new(GDK_COLORSPACE_RGB/*colorspace*/,
162 TRUE/*has_alpha*/,
163 8/*bits_per_sample*/,
164 e->width * ICON_WIDTH,
165 e->height * ICON_HEIGHT);
166 gdk_pixbuf_fill(pixbuf_view, 0x00000000);
167
168 app = app_list_first(e->apwal->apps);
169 while (app)
170 {
171 is_valid = app_is_executable(app);
172 editor_draw_icon(e, pixbuf_view, app->icon, app->x, app->y, is_valid,
173 FALSE/*cursor?*/);
174 app = app_list_next(e->apwal->apps);
175 } // end while app
176
177 // draw cursor
178 editor_draw_icon(e, pixbuf_view, "cursor2", 0, 0, TRUE, TRUE/*cursor?*/);
179 // draw selection around the current selected icon
180 if (e->apwal->selected_app != NULL)
181 editor_draw_icon(e, pixbuf_view, "selected", e->apwal->selected_app->x,
182 e->apwal->selected_app->y, TRUE, TRUE/*cursor?*/);
183 //editor_draw_icon(e, pixbuf_view, "selected", e->drag_current_x,
184 // e->drag_current_y, TRUE, TRUE /*cursor?*/);
185
186 gtk_image_set_from_pixbuf(GTK_IMAGE(e->image), pixbuf_view);
187 g_object_unref(pixbuf_view);
188
189 }
190 // ----------------------------------------------------------------------------
editor_build_interface(editor_t * e)191 static void editor_build_interface(editor_t *e)
192 {
193 //GtkWidget *notebook;
194 //GtkWidget *editor_vbox;
195 //GtkWidget *editor_frame;
196 GtkWidget *table;
197 GtkWidget *eventbox;
198 //GtkWidget *image;
199 GtkWidget *button; // left, right, top, button & ?
200 GtkWidget *image; // ? img
201 GtkWidget *eventbox_for_image; // ? img
202 char *tooltips_str;
203
204 g_assert(e != NULL);
205 g_assert(e->apwal != NULL);
206 g_assert(e->apwal->editor_frame != NULL);
207
208 // table for the editor view
209 table = gtk_table_new(3/*rows*/,3/*cols*/, FALSE/*homogeous*/);
210 gtk_container_add(GTK_CONTAINER(e->apwal->editor_frame), table);
211 gtk_widget_show(table);
212
213 // arrows ^ v < > and '?' for the tips
214 button = gtk_arrow_button_new(GTK_ARROW_UP);
215 gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 0, 1);
216 g_signal_connect(G_OBJECT(button), "clicked",
217 G_CALLBACK(editor_arrow_up_clicked), e);
218 gtk_widget_show(button);
219 button = gtk_arrow_button_new(GTK_ARROW_DOWN);
220 gtk_table_attach_defaults(GTK_TABLE(table), button, 1, 2, 2, 3);
221 g_signal_connect(G_OBJECT(button), "clicked",
222 G_CALLBACK(editor_arrow_down_clicked), e);
223 gtk_widget_show(button);
224 button = gtk_arrow_button_new(GTK_ARROW_LEFT);
225 gtk_table_attach_defaults(GTK_TABLE(table), button, 0, 1, 1, 2);
226 g_signal_connect(G_OBJECT(button), "clicked",
227 G_CALLBACK(editor_arrow_left_clicked), e);
228 gtk_widget_show(button);
229 button = gtk_arrow_button_new(GTK_ARROW_RIGHT);
230 gtk_table_attach_defaults(GTK_TABLE(table), button, 2, 3, 1, 2);
231 g_signal_connect(G_OBJECT(button), "clicked",
232 G_CALLBACK(editor_arrow_right_clicked), e);
233 gtk_widget_show(button);
234
235 eventbox_for_image = gtk_event_box_new();
236 gtk_table_attach_defaults(GTK_TABLE(table), eventbox_for_image, 0, 1, 0, 1);
237 gtk_widget_show(eventbox_for_image);
238 tooltips_str = "Editor behaviours\n"
239 ". Double click on an icon to select a new one\n"
240 ". Drag and drop an icon to move it on the grid\n"
241 ". Press the remove button to delete an application\n"
242 ". Use the arrow buttons to see all the grid\n"
243 ". The red cross is the positon of the cursor";
244 gtk_tooltips_set_tip(e->apwal->tips, eventbox_for_image, tooltips_str, NULL);
245 image = gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO,
246 GTK_ICON_SIZE_MENU);
247 // GTK_ICON_SIZE_SMALL_TOOLBAR);
248 gtk_container_add(GTK_CONTAINER(eventbox_for_image), image);
249 gtk_widget_show(image);
250
251 // eventbox
252 eventbox = gtk_event_box_new();
253 g_signal_connect(G_OBJECT(eventbox), "button_press_event",
254 G_CALLBACK(editor_button_press_event), e);
255 g_signal_connect(G_OBJECT(eventbox), "button_release_event",
256 G_CALLBACK(editor_button_release_event), e);
257 g_signal_connect(G_OBJECT(eventbox), "motion_notify_event",
258 G_CALLBACK(editor_motion_notify_event), e);
259 gtk_table_attach_defaults(GTK_TABLE(table), eventbox, 1, 2, 1, 2);
260 gtk_widget_show(eventbox);
261
262 // image
263 e->image = gtk_image_new();
264 gtk_container_add(GTK_CONTAINER(eventbox), e->image);
265 gtk_widget_show(e->image);
266
267 //gtk_widget_set_extension_events(eventbox, GDK_EXTENSION_EVENTS_ALL);
268 //gtk_widget_add_events(eventbox, GDK_ALL_EVENTS_MASK | GDK_KEY_RELEASE_MASK);
269 //GTK_WIDGET_SET_FLAGS (eventbox, GTK_CAN_FOCUS);
270 //g_signal_connect(G_OBJECT(eventbox), "key_release_event",
271 // G_CALLBACK(editor_key_release_event), e);
272
273 //gtk_notebook_append_page(GTK_NOTEBOOK(e->apwal->notebook),
274 // editor_vbox, editor_label);
275 }
276 // ----------------------------------------------------------------------------
277 // ----------------------------------------------------------------------------
editor_button_press_event(GtkWidget * widget,GdkEventButton * event,editor_t * e)278 static gboolean editor_button_press_event(GtkWidget *widget,
279 GdkEventButton *event,
280 editor_t *e)
281 {
282 gint x, y;
283 app_t *app;
284 g_assert(widget != NULL && e != NULL && event != NULL);
285 g_assert(e->apwal != NULL);
286
287 // have the keyboard focus on the eventbox to be able to catch key bindings
288 gtk_widget_grab_focus(widget);
289
290 x = (((int)event->x)/ICON_WIDTH) + e->x;
291 y = (((int)event->y)/ICON_WIDTH) + e->y;
292 TRACE("x:%d, y:%d", x, y);
293 app = app_list_at_xy(e->apwal->apps, x, y);
294 if (app != NULL)
295 e->drag_in_progress = TRUE;
296 else
297 e->drag_in_progress = FALSE;
298
299 e->drag_start_x = x;
300 e->drag_start_y = y;
301 e->drag_current_x = x;
302 e->drag_current_y = y;
303
304 if (app != NULL)
305 {
306 e->apwal->selected_app = app;
307 apwalapp_selected_app_modified(e->apwal);
308 }
309 editor_refresh(e);
310 return TRUE;
311 }
312 // ----------------------------------------------------------------------------
editor_motion_notify_event(GtkWidget * widget,GdkEventMotion * event,editor_t * e)313 static gboolean editor_motion_notify_event(GtkWidget *widget,
314 GdkEventMotion *event,
315 editor_t *e)
316 {
317 gint x, y;
318 g_assert(widget != NULL && e != NULL && event != NULL);
319
320 x = (((int)event->x)/ICON_WIDTH) + e->x;
321 y = (((int)event->y)/ICON_WIDTH) + e->y;
322 if ( ! ((x == e->drag_current_x) && (y == e->drag_current_y)) ) //||
323 // ((x == e->drag_start_x) && (y == e->drag_start_y)) ) )
324 {
325 e->drag_current_x = (((int)event->x)/ICON_WIDTH) + e->x;
326 e->drag_current_y = (((int)event->y)/ICON_WIDTH) + e->y;
327 TRACE("x:%d, y:%d", e->drag_current_x, e->drag_current_y);
328 editor_refresh(e);
329 }
330 return TRUE;
331 }
332 // ----------------------------------------------------------------------------
333 // -----------------------------------------------------------------------------
editor_is_dblclick(editor_t * e,int x,int y)334 static gboolean editor_is_dblclick(editor_t *e, int x, int y)
335 {
336 gint elapse;
337 g_assert(e != NULL);
338 if (e->dblclick_timer == NULL)
339 {
340 e->dblclick_timer = g_timer_new();
341 e->dblclick_last_x = x;
342 e->dblclick_last_y = y;
343 return FALSE;
344 }
345 elapse = (int)(g_timer_elapsed(e->dblclick_timer, NULL) * 1000);
346 g_timer_start(e->dblclick_timer);
347 if ((e->dblclick_last_x != x) || (e->dblclick_last_y != y))
348 {
349 e->dblclick_last_x = x;
350 e->dblclick_last_y = y;
351 return FALSE;
352 }
353 if (elapse > DBLCLICK)
354 return FALSE;
355
356 return TRUE;
357 }
358 // -----------------------------------------------------------------------------
editor_button_release_event(GtkWidget * widget,GdkEventButton * event,editor_t * e)359 static gboolean editor_button_release_event(GtkWidget *widget,
360 GdkEventButton *event,
361 editor_t *e)
362 {
363 int pos_x;
364 int pos_y;
365 app_t *app, *newapp;
366 gboolean drag_in_progress;
367
368 g_assert(e != NULL);
369 g_assert(e->apwal != NULL);
370 g_assert(e->apwal->apps != NULL);
371
372 TRACE("type:%x, x:%f, y:%f, button:%d",
373 event->type, event->x, event->y, event->button);
374
375 pos_x = (((int)event->x)/ICON_WIDTH) + e->x;
376 pos_y = (((int)event->y)/ICON_WIDTH) + e->y;
377 drag_in_progress = e->drag_in_progress;
378 e->drag_in_progress = FALSE;
379 // on double click on a app, go to icon selection tab to select the icon
380 // if no app on the position of the double click, do nothing
381 if (editor_is_dblclick(e, pos_x, pos_y) == TRUE)
382 {
383 if (e->apwal->selected_app != NULL)
384 apwalapp_goto_iconsel(e->apwal);
385 return TRUE;
386 }
387
388 // if drag in progress and the cursor had moved then do the drop
389 // on the new location of the cursor
390 if ((drag_in_progress == TRUE) &&
391 ( ! ((pos_x == e->drag_start_x) && (pos_y == e->drag_start_y)) ) )
392 {
393 // if there are no app on the drop posistion, move the app
394 // dragged to the new position, else nothing to do
395 app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
396 if (app == NULL)
397 {
398 newapp = app_list_at_xy(e->apwal->apps, e->drag_start_x, e->drag_start_y);
399 if (newapp == NULL)
400 ERR("app_list_at_xy, e->drag_start_x:%d, e->drag_start_y:%d",
401 e->drag_start_x, e->drag_start_y);
402 newapp->x = pos_x;
403 newapp->y = pos_y;
404 e->drag_start_x = pos_x; // so we see the app 'selected' in the editor
405 e->drag_start_y = pos_y;
406 e->drag_current_x = pos_x; // so we see the app 'selected' in the editor
407 e->drag_current_y = pos_y;
408 }
409 editor_refresh(e);
410 return TRUE;
411 }
412 // if the cursor has moved like and drag and drop but no drag in progress,
413 // then do nothing (case of a drag from an empty position)
414 if ((drag_in_progress == FALSE) &&
415 ( ! ((pos_x == e->drag_start_x) && (pos_y == e->drag_start_y)) ) )
416 return TRUE;
417
418
419 // case of the simple clic
420 editor_goto(e, pos_x, pos_y);
421 return TRUE;
422 }
423
424 // -----------------------------------------------------------------------------
425 // selected app and show it in property.
editor_goto(editor_t * e,int pos_x,int pos_y)426 void editor_goto(editor_t *e, int pos_x, int pos_y)
427 {
428 int cc;
429 app_t *app, *newapp;
430 // if no app on the current position then move the noname app if not
431 // created. if no noname app found create one.
432 // if an app is found check if a noname app exist and remove it.
433 // then select the found app and refresh the properties.
434 app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
435 if (app == NULL)
436 {
437 newapp = app_list_find_noname(e->apwal->apps);
438 if (newapp != NULL)
439 {
440 newapp->x = pos_x;
441 newapp->y = pos_y;
442 }
443 else
444 {
445 newapp = app_new_noname(pos_x, pos_y);
446 app_list_add(e->apwal->apps, newapp);
447 }
448 app = app_list_at_xy(e->apwal->apps, pos_x, pos_y);
449 if (app != newapp)
450 ERR("app:%p != newapp:%p", app, newapp);
451 }
452 else
453 {
454 newapp = app_list_find_noname(e->apwal->apps);
455 if (newapp != NULL && newapp != app)
456 {
457 cc = app_list_remove(e->apwal->apps, newapp);
458 if (cc)
459 ERR("%s", "impossible to remove the noname app found just before.");
460 }
461 }
462 e->apwal->selected_app = app;
463 editor_refresh(e);
464 apwalapp_selected_app_modified(e->apwal);
465 }
466 // ----------------------------------------------------------------------------
editor_arrow_up_clicked(GtkWidget * widget,editor_t * e)467 static void editor_arrow_up_clicked(GtkWidget *widget, editor_t *e)
468 {
469 g_assert(e != NULL);
470 //TRACE("%s", "");
471 e->y--;
472 editor_refresh(e);
473 }
474 // ----------------------------------------------------------------------------
editor_arrow_down_clicked(GtkWidget * widget,editor_t * e)475 static void editor_arrow_down_clicked(GtkWidget *widget, editor_t *e)
476 {
477 g_assert(e != NULL);
478 //TRACE("%s", "");
479 e->y++;
480 editor_refresh(e);
481 }
482 // ----------------------------------------------------------------------------
editor_arrow_left_clicked(GtkWidget * widget,editor_t * e)483 static void editor_arrow_left_clicked(GtkWidget *widget, editor_t *e)
484 {
485 g_assert(e != NULL);
486 //TRACE("%s", "");
487 e->x--;
488 editor_refresh(e);
489 }
490 // ----------------------------------------------------------------------------
editor_arrow_right_clicked(GtkWidget * widget,editor_t * e)491 static void editor_arrow_right_clicked(GtkWidget *widget, editor_t *e)
492 {
493 g_assert(e != NULL);
494 //TRACE("%s", "");
495 e->x++;
496 editor_refresh(e);
497 }
498 // ----------------------------------------------------------------------------
499